diff --git a/.editorconfig b/.editorconfig index e57811609fb..ea5ecdce71a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,8 +9,11 @@ insert_final_newline = false max_line_length = 120 tab_width = 4 -[*mixins*.json] +[{*.json,*.jsonc,*.png.mcmeta,mcmod.info,pack.mcmeta}] indent_size = 2 [*.yaml] -indent_size = 2 \ No newline at end of file +indent_size = 2 + +[verification-metadata.xml] +indent_size = 3 diff --git a/.github/workflows/check-spotless.yaml b/.github/workflows/check-spotless.yaml index 5919a3117ef..96a95759998 100644 --- a/.github/workflows/check-spotless.yaml +++ b/.github/workflows/check-spotless.yaml @@ -10,6 +10,6 @@ jobs: call-check: uses: SpongePowered/.github/.github/workflows/shared-check-spotless.yaml@master with: - runtime_version: 21 - extra_gradle_params: "-Pprojects=vanilla,forge,neoforge,testplugins" + runtime_version: 25 + extra_gradle_params: "-Pprojects=vanilla,neoforge,testplugins" secrets: inherit diff --git a/.github/workflows/common-integration-test.yaml b/.github/workflows/common-integration-test.yaml index befe39d8ea2..626e1c94897 100644 --- a/.github/workflows/common-integration-test.yaml +++ b/.github/workflows/common-integration-test.yaml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-22.04, windows-latest, macos-latest] - java: [ 21 ] + java: [ 25 ] runs-on: "${{ matrix.os }}" steps: - name: Check out repository to use the build.gradle.kts as a hash file @@ -32,7 +32,7 @@ jobs: with: distribution: temurin java-version: "${{ matrix.java }}" - - uses: "actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf" # v4.2.2 + - uses: "actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0 with: path: "${{ github.workspace}}/libraries" key: "${{runner.os}}-${{matrix.java}}-it-libraries-${{ hashFiles('code/build.gradle.kts') }}-${{ github.workspace }}/code/libraries.json" diff --git a/.github/workflows/common-run-build.yaml b/.github/workflows/common-run-build.yaml index 0cc1cfb0873..f73c909eb1e 100644 --- a/.github/workflows/common-run-build.yaml +++ b/.github/workflows/common-run-build.yaml @@ -11,15 +11,31 @@ jobs: # Only run on PRs if the source branch is on someone else's repo if: "${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}" runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + project: + - vanilla + - forge + - neoforge + include: + - project: vanilla + gradleProject: SpongeVanilla + - project: forge + gradleProject: SpongeForge + - project: neoforge + gradleProject: SpongeNeo steps: - name: setup id: setup uses: SpongePowered/.github/.github/actions/setup-java-env@master + if: "!startsWith(github.ref, 'refs/heads/update/') || matrix.project == 'vanilla'" with: - runtime_version: 21 + runtime_version: 25 publishing_branch_regex: 'api-\d+' - name: setup / cache minecraft dependencies - uses: "actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf" # v4.2.2 + uses: "actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0 + if: "!startsWith(github.ref, 'refs/heads/update/') || matrix.project == 'vanilla'" with: path: | ${{ github.workspace}}/buildSrc/.gradle/ @@ -28,40 +44,33 @@ jobs: restore-keys: | ${{ runner.os }}-minecraft-${{ env.CACHE_REV }}- - name: setup / environment + if: "!startsWith(github.ref, 'refs/heads/update/') || matrix.project == 'vanilla'" run: | echo "GIT_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV echo "GIT_BRANCH=${GITHUB_REF##*/}" >> $GITHUB_ENV echo "BUILD_NUMBER=${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV - name: build with Gradle - run: ./gradlew -Pprojects=vanilla,forge,neoforge,testplugins build --stacktrace + if: "!startsWith(github.ref, 'refs/heads/update/') || matrix.project == 'vanilla'" + run: ./gradlew -Pprojects=${{ matrix.project }},testplugins build --stacktrace env: CI_SYSTEM: Github Actions - name: Archive artifacts for build + if: "!startsWith(github.ref, 'refs/heads/update/') || matrix.project == 'vanilla'" uses: "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" # v4.3.1 with: - name: Sponge Jars + name: Sponge ${{ matrix.project }} Jars path: | ${{ github.workspace }}/SpongeAPI/build/libs/*.jar ${{ github.workspace }}/build/libs/*.jar - ${{ github.workspace }}/vanilla/build/libs/*.jar - ${{ github.workspace }}/forge/build/libs/*.jar - ${{ github.workspace }}/neoforge/build/libs/*.jar - - name: SpongeVanilla Production Jar + ${{ github.workspace }}/${{ matrix.project }}/build/libs/*.jar + - name: ${{ matrix.gradleProject }} Production Jar + if: "!startsWith(github.ref, 'refs/heads/update/') || matrix.project == 'vanilla'" uses: "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" # v4.3.1 with: - name: SpongeVanilla Production Jar - path: "${{ github.workspace }}/vanilla/build/libs/*-universal.jar" - - name: SpongeForge Production Jar - uses: "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" # v4.3.1 - with: - name: SpongeForge Production Jar - path: "${{ github.workspace }}/forge/build/libs/*-universal.jar" - - name: SpongeNeo Production Jar - uses: "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" # v4.3.1 - with: - name: SpongeNeo Production Jar - path: "${{ github.workspace }}/neoforge/build/libs/*-universal.jar" + name: ${{ matrix.gradleProject }} Production Jar + path: "${{ github.workspace }}/${{ matrix.project }}/build/libs/*-universal.jar" - name: SpongeVanilla libraries + if: "matrix.project == 'vanilla'" uses: "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" # v4.3.1 with: name: SpongeVanilla installer libraries diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 90f15aa233e..27f4f5fae7e 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -7,7 +7,7 @@ on: push: branches: - 'api-*' - - update/* + - 'update/*' tags-ignore: [ '**' ] env: @@ -24,7 +24,6 @@ jobs: publish: needs: integrationTest runs-on: ubuntu-22.04 - # This enables publishing individual projects in parallel strategy: fail-fast: false matrix: @@ -52,11 +51,11 @@ jobs: id: setup uses: SpongePowered/.github/.github/actions/setup-java-env@master with: - runtime_version: 21 + runtime_version: 25 publishing_branch_regex: '' - name: setup / minecraft cache if: "!(matrix.skipUpdateBranch && startsWith(github.ref, 'refs/heads/update/'))" - uses: "actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf" # v4.2.2 + uses: "actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0 with: path: | ${{ github.workspace}}/buildSrc/.gradle/ diff --git a/.gitignore b/.gitignore index 3bbc4a6de24..ec8cc3fd6d3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ dist lib out run +## ForgeGradle 7 moves the run configurations to runs instead of run. +runs +run_snap /target *.com *.class @@ -75,10 +78,10 @@ nb-configuration.xml /.apt_generated/ *.launch /.nb-gradle/ +/.vscode/ + # We need to exclude the copied files. buildSrc/src/api -# forge-loom always generates this for some reason... -/.vscode/ # This is to optionally enable testplugins in development. # the test plugins themselves can be committed as examples. diff --git a/README.md b/README.md index f02f97debe8..e20543e2ae4 100644 --- a/README.md +++ b/README.md @@ -15,19 +15,19 @@ The SpongeAPI implementation targeting vanilla Minecraft and 3rd party platforms [![SpongeVanilla Latest](https://img.shields.io/nexus/maven-releases/org.spongepowered/spongevanilla?label=Latest&server=https%3A%2F%2Frepo.spongepowered.org%2F)](https://repo.spongepowered.org/#browse/search=attributes.maven2.artifactId%3Dspongevanilla%20AND%20attributes.maven2.groupId%3Dorg.spongepowered) -[![SpongeVanilla 1.21.1](https://img.shields.io/nexus/maven-releases/org.spongepowered/spongevanilla?label=1.21.1&queryOpt=%3Aversion%3D1.21.1-*&server=https%3A%2F%2Frepo.spongepowered.org%2F)](https://repo.spongepowered.org/#browse/search=attributes.maven2.artifactId%3Dspongevanilla%20AND%20attributes.maven2.groupId%3Dorg.spongepowered%20AND%20version%3D1.21.1-*) +[![SpongeVanilla 26.2](https://img.shields.io/nexus/maven-releases/org.spongepowered/spongevanilla?label=26.2&queryOpt=%3Aversion%3D26.2-*&server=https%3A%2F%2Frepo.spongepowered.org%2F)](https://repo.spongepowered.org/#browse/search=attributes.maven2.artifactId%3Dspongevanilla%20AND%20attributes.maven2.groupId%3Dorg.spongepowered%20AND%20version%3D26.2-*) ### SpongeForge [![SpongeForge Latest](https://img.shields.io/nexus/maven-releases/org.spongepowered/spongeforge?label=Latest&server=https%3A%2F%2Frepo.spongepowered.org%2F)](https://repo.spongepowered.org/#browse/search=attributes.maven2.artifactId%3Dspongeforge%20AND%20attributes.maven2.groupId%3Dorg.spongepowered) -[![SpongeForge 1.21.1](https://img.shields.io/nexus/maven-releases/org.spongepowered/spongeforge?label=1.21.1&queryOpt=%3Aversion%3D1.21.1-*&server=https%3A%2F%2Frepo.spongepowered.org%2F)](https://repo.spongepowered.org/#browse/search=attributes.maven2.artifactId%3Dspongeforge%20AND%20attributes.maven2.groupId%3Dorg.spongepowered%20AND%20version%3D1.21.1-*) +[![SpongeForge 26.2](https://img.shields.io/nexus/maven-releases/org.spongepowered/spongeforge?label=26.2&queryOpt=%3Aversion%3D26.2-*&server=https%3A%2F%2Frepo.spongepowered.org%2F)](https://repo.spongepowered.org/#browse/search=attributes.maven2.artifactId%3Dspongeforge%20AND%20attributes.maven2.groupId%3Dorg.spongepowered%20AND%20version%3D26.2-*) ### SpongeNeo [![SpongeNeo Latest](https://img.shields.io/nexus/maven-releases/org.spongepowered/spongeneo?label=Latest&server=https%3A%2F%2Frepo.spongepowered.org%2F)](https://repo.spongepowered.org/#browse/search=attributes.maven2.artifactId%3Dspongeneo%20AND%20attributes.maven2.groupId%3Dorg.spongepowered) -[![SpongeNeo 1.21.1](https://img.shields.io/nexus/maven-releases/org.spongepowered/spongeneo?label=1.21.1&queryOpt=%3Aversion%3D1.21.1-*&server=https%3A%2F%2Frepo.spongepowered.org%2F)](https://repo.spongepowered.org/#browse/search=attributes.maven2.artifactId%3Dspongeneo%20AND%20attributes.maven2.groupId%3Dorg.spongepowered%20AND%20version%3D1.21.1-*) +[![SpongeNeo 26.2](https://img.shields.io/nexus/maven-releases/org.spongepowered/spongeneo?label=26.2&queryOpt=%3Aversion%3D26.2-*&server=https%3A%2F%2Frepo.spongepowered.org%2F)](https://repo.spongepowered.org/#browse/search=attributes.maven2.artifactId%3Dspongeneo%20AND%20attributes.maven2.groupId%3Dorg.spongepowered%20AND%20version%3D26.2-*) ## Prerequisites @@ -42,7 +42,7 @@ The following steps will ensure your project is cloned properly. ## Setup **Note**: Sponge uses [Gradle] as its build system. The repo includes the Gradle wrapper that will automatically download the correct Gradle -version. Local installations of Gradle may work (as long as they are using Gradle 8.7+) but are untested. To execute the Gradle wrapper, run the +version. Local installations of Gradle may work (as long as they are using Gradle 8.11.1+) but are untested. To execute the Gradle wrapper, run the `./gradlew` script on Unix systems or only `gradlew` on Windows systems. To have browsable sources for use in-IDE, run `./gradlew :decompile`. This command will need to be re-ran after any change to diff --git a/SpongeAPI b/SpongeAPI index fb073c867e3..ba75ff0a383 160000 --- a/SpongeAPI +++ b/SpongeAPI @@ -1 +1 @@ -Subproject commit fb073c867e3f8d9da87acba15a6178bc261badad +Subproject commit ba75ff0a383c2a2e7bd6b082bfa7db59c2d8d8e5 diff --git a/bootstrap/build.gradle.kts b/bootstrap/build.gradle.kts index beea2d28538..b5ea55595d5 100644 --- a/bootstrap/build.gradle.kts +++ b/bootstrap/build.gradle.kts @@ -17,5 +17,5 @@ dependencies { forgeLib(libs.securemodules) val neoforgeLib = neoforge.implementationConfigurationName - neoforgeLib(libs.securejarhandler) + neoforgeLib(libs.fancymodloader) } diff --git a/bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/ForgeLikeBootstrap.java b/bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/ForgeLikeBootstrap.java index f7fbd101d01..d145109b587 100644 --- a/bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/ForgeLikeBootstrap.java +++ b/bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/ForgeLikeBootstrap.java @@ -28,43 +28,153 @@ import net.minecraftforge.securemodules.SecureModuleClassLoader; import net.minecraftforge.securemodules.SecureModuleFinder; import org.spongepowered.bootstrap.Bootstrap; +import org.spongepowered.bootstrap.dev.DevClasspath; +import org.spongepowered.bootstrap.forge.classloader.FilteringPassthroughClassLoader; +import org.spongepowered.bootstrap.forge.classloader.ResourceClassLoader; +import java.io.File; import java.lang.module.Configuration; import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import java.net.URL; import java.nio.file.Path; -import java.util.Collection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; -public abstract class ForgeLikeBootstrap extends Bootstrap { +public abstract class ForgeLikeBootstrap { private final String[] args; public ForgeLikeBootstrap(final String[] args) { this.args = args; } - @Override - protected SecureJar createJar(final Path[] paths) { - return SecureJar.from(paths); + public abstract String name(); + + /** + * When filtered, the jar content is still accessible via the application classloader but is not loaded as a module. + */ + protected boolean filterApplicationModule(final SecureJar jar) { + return false; } - @Override - protected String getModuleName(final SecureJar jar) { - return jar.name(); + protected void runApplication(final ModuleLayer layer) throws Exception { + final Class appClass = layer.findLoader("cpw.mods.modlauncher").loadClass("cpw.mods.modlauncher.Launcher"); + appClass.getDeclaredMethod("main", String[].class).invoke(null, (Object) this.args); } - @Override - protected ModuleFinder createModuleFinder(final Collection jars) { - return SecureModuleFinder.of(jars); + public void devBoot(final boolean isolated) throws Exception { + this.boot(DevClasspath.resolve(), isolated); } - @Override - protected ClassLoader createApplicationClassLoader(final Configuration config, final List parentLayers, final ClassLoader parentLoader) { - return new SecureModuleClassLoader("APP-BOOTSTRAP", null, config, parentLayers, List.of(parentLoader)); + public void boot(final List classpath, final boolean isolated) throws Exception { + if (Bootstrap.DEBUG) { + System.out.println("Bootstrapping " + this.name() + " from classloader: " + getClass().getClassLoader()); + System.out.println("Isolated: " + isolated); + } + + // Collect the jars + final Set moduleNames = new HashSet<>(); + final List resourcePaths = new ArrayList<>(); + final List resourceJars = new ArrayList<>(); + final List appJars = new ArrayList<>(); + + for (final Path[] paths : classpath) { + final SecureJar jar = SecureJar.from(paths); + final String name = jar.name(); + + // Ignore modules already present + if (ModuleLayer.boot().findModule(name).isPresent()) { + if (Bootstrap.DEBUG) { + System.out.println("Boot: " + name + " " + ForgeLikeBootstrap.formatUnion(paths)); + } + continue; + } + + if (this.filterApplicationModule(jar)) { + if (Bootstrap.DEBUG) { + System.out.println("Filtered: " + name + " " + ForgeLikeBootstrap.formatUnion(paths)); + } + resourceJars.add(jar); + resourcePaths.addAll(Arrays.asList(paths)); + continue; + } + + if (!moduleNames.add(name)) { + if (Bootstrap.DEBUG) { + System.out.println("Duplicate: " + name + " " + ForgeLikeBootstrap.formatUnion(paths)); + } + resourceJars.add(jar); + resourcePaths.addAll(Arrays.asList(paths)); + continue; + } + + if (Bootstrap.DEBUG) { + System.out.println("App: " + name + " " + ForgeLikeBootstrap.formatUnion(paths)); + } + appJars.add(jar); + } + + // Create the layer configuration + final ModuleFinder finder = SecureModuleFinder.of(appJars); + final Configuration config = ModuleLayer.boot().configuration().resolveAndBind(finder, ModuleFinder.ofSystem(), moduleNames); + final List parentLayers = List.of(ModuleLayer.boot()); + + final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + + // Isolation + ClassLoader parentLoader; + if (isolated) { + parentLoader = ClassLoader.getPlatformClassLoader(); + } else { + // Don't leak classes that we manage already + final List modules = new ArrayList<>(); + modules.addAll(finder.findAll()); + modules.addAll(SecureModuleFinder.of(resourceJars).findAll()); + + final String resources = System.getProperty("sponge.resources"); + if (resources != null) { + final List spongeJars = new ArrayList<>(); + for (final String entry : resources.split(File.pathSeparator)) { + final Path[] paths = Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new); + spongeJars.add(SecureJar.from(paths)); + } + modules.addAll(SecureModuleFinder.of(spongeJars).findAll()); + } + + parentLoader = new FilteringPassthroughClassLoader(contextLoader, modules); + } + + // Intermediate classloader to include resources but not modules + if (!resourcePaths.isEmpty()) { + final URL[] urls = new URL[resourcePaths.size()]; + for (int i = 0; i < urls.length; i++) { + urls[i] = resourcePaths.get(i).toUri().toURL(); + } + parentLoader = new ResourceClassLoader("BOOTSTRAP-RESOURCES", urls, parentLoader); + } + + // Create the application classloader + final ClassLoader loader = new SecureModuleClassLoader("APP-BOOTSTRAP", null, config, parentLayers, List.of(parentLoader)); + final ModuleLayer layer = ModuleLayer.boot().defineModules(config, moduleName -> loader); + + // Run the application + try { + Thread.currentThread().setContextClassLoader(loader); + if (Bootstrap.DEBUG) { + System.out.println("Starting application ..."); + } + this.runApplication(layer); + } finally { + Thread.currentThread().setContextClassLoader(contextLoader); + } } - @Override - protected void runApplication(final ModuleLayer layer) throws Exception { - final Class appClass = layer.findModule("cpw.mods.modlauncher").get().getClassLoader().loadClass("cpw.mods.modlauncher.Launcher"); - appClass.getDeclaredMethod("main", String[].class).invoke(null, (Object) this.args); + public static String formatUnion(final Path[] paths) { + return Arrays.stream(paths).map(Path::toString).collect(Collectors.joining("&")); } } diff --git a/bootstrap/src/main/java/org/spongepowered/bootstrap/FilteringPassthroughClassLoader.java b/bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/classloader/FilteringPassthroughClassLoader.java similarity index 93% rename from bootstrap/src/main/java/org/spongepowered/bootstrap/FilteringPassthroughClassLoader.java rename to bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/classloader/FilteringPassthroughClassLoader.java index e01669d038f..39064a6b126 100644 --- a/bootstrap/src/main/java/org/spongepowered/bootstrap/FilteringPassthroughClassLoader.java +++ b/bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/classloader/FilteringPassthroughClassLoader.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.bootstrap; +package org.spongepowered.bootstrap.forge.classloader; import java.lang.module.ModuleReference; import java.util.Collection; @@ -37,7 +37,7 @@ public final class FilteringPassthroughClassLoader extends ClassLoader { ClassLoader.registerAsParallelCapable(); } - FilteringPassthroughClassLoader(final ClassLoader parent, final Collection modules) { + public FilteringPassthroughClassLoader(final ClassLoader parent, final Collection modules) { super(parent); modules.forEach(m -> this.filteredPackages.addAll(m.descriptor().packages())); } diff --git a/bootstrap/src/main/java/org/spongepowered/bootstrap/ResourceClassLoader.java b/bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/classloader/ResourceClassLoader.java similarity index 96% rename from bootstrap/src/main/java/org/spongepowered/bootstrap/ResourceClassLoader.java rename to bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/classloader/ResourceClassLoader.java index ee11b705544..0aa8ade915d 100644 --- a/bootstrap/src/main/java/org/spongepowered/bootstrap/ResourceClassLoader.java +++ b/bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/classloader/ResourceClassLoader.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.bootstrap; +package org.spongepowered.bootstrap.forge.classloader; import java.net.URL; import java.net.URLClassLoader; diff --git a/bootstrap/src/main/java/org/spongepowered/bootstrap/Bootstrap.java b/bootstrap/src/main/java/org/spongepowered/bootstrap/Bootstrap.java index d00e380c352..55c77fbaf01 100644 --- a/bootstrap/src/main/java/org/spongepowered/bootstrap/Bootstrap.java +++ b/bootstrap/src/main/java/org/spongepowered/bootstrap/Bootstrap.java @@ -24,153 +24,6 @@ */ package org.spongepowered.bootstrap; -import org.spongepowered.bootstrap.dev.DevClasspath; - -import java.io.File; -import java.lang.module.Configuration; -import java.lang.module.ModuleFinder; -import java.lang.module.ModuleReference; -import java.net.URL; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public abstract class Bootstrap { +public class Bootstrap { public static final boolean DEBUG = Boolean.getBoolean("sponge.bootstrap.debug"); - - public abstract String name(); - - protected abstract Jar createJar(Path[] paths); - - protected abstract String getModuleName(Jar jar); - - protected abstract ModuleFinder createModuleFinder(Collection jars); - - /** - * When filtered, the jar content is still accessible via the application classloader but is not loaded as a module. - */ - protected boolean filterApplicationModule(final Jar jar) { - return false; - } - - protected abstract ClassLoader createApplicationClassLoader(Configuration config, List parentLayers, ClassLoader parentLoader); - - protected abstract void runApplication(ModuleLayer layer) throws Exception; - - public void devBoot(final boolean isolated) throws Exception { - this.boot(DevClasspath.resolve(), isolated); - } - - public void boot(final List classpath, final boolean isolated) throws Exception { - if (Bootstrap.DEBUG) { - System.out.println("Bootstrapping " + this.name() + " from classloader: " + getClass().getClassLoader()); - System.out.println("Isolated: " + isolated); - } - - // Collect the jars - final Set moduleNames = new HashSet<>(); - final List resourcePaths = new ArrayList<>(); - final List resourceJars = new ArrayList<>(); - final List appJars = new ArrayList<>(); - - for (final Path[] paths : classpath) { - final Jar jar = this.createJar(paths); - final String name = this.getModuleName(jar); - - // Ignore modules already present - if (ModuleLayer.boot().findModule(name).isPresent()) { - if (Bootstrap.DEBUG) { - System.out.println("Boot: " + name + " " + Bootstrap.formatUnion(paths)); - } - continue; - } - - if (this.filterApplicationModule(jar)) { - if (Bootstrap.DEBUG) { - System.out.println("Filtered: " + name + " " + Bootstrap.formatUnion(paths)); - } - resourceJars.add(jar); - resourcePaths.addAll(Arrays.asList(paths)); - continue; - } - - if (!moduleNames.add(name)) { - if (Bootstrap.DEBUG) { - System.out.println("Duplicate: " + name + " " + Bootstrap.formatUnion(paths)); - } - resourceJars.add(jar); - resourcePaths.addAll(Arrays.asList(paths)); - continue; - } - - if (Bootstrap.DEBUG) { - System.out.println("App: " + name + " " + Bootstrap.formatUnion(paths)); - } - appJars.add(jar); - } - - // Create the layer configuration - final ModuleFinder finder = this.createModuleFinder(appJars); - final Configuration config = ModuleLayer.boot().configuration().resolveAndBind(finder, ModuleFinder.ofSystem(), moduleNames); - final List parentLayers = List.of(ModuleLayer.boot()); - - final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); - - // Isolation - ClassLoader parentLoader; - if (isolated) { - parentLoader = ClassLoader.getPlatformClassLoader(); - } else { - // Don't leak classes that we manage already - final List modules = new ArrayList<>(); - modules.addAll(finder.findAll()); - modules.addAll(this.createModuleFinder(resourceJars).findAll()); - - final String resources = System.getProperty("sponge.resources"); - if (resources != null) { - final List spongeJars = new ArrayList<>(); - for (final String entry : resources.split(File.pathSeparator)) { - final Path[] paths = Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new); - spongeJars.add(this.createJar(paths)); - } - modules.addAll(this.createModuleFinder(spongeJars).findAll()); - } - - parentLoader = new FilteringPassthroughClassLoader(contextLoader, modules); - } - - // Intermediate classloader to include resources but not modules - if (!resourcePaths.isEmpty()) { - final URL[] urls = new URL[resourcePaths.size()]; - for (int i = 0; i < urls.length; i++) { - urls[i] = resourcePaths.get(i).toUri().toURL(); - } - parentLoader = new ResourceClassLoader("BOOTSTRAP-RESOURCES", urls, parentLoader); - } - - // Create the application classloader - final ClassLoader loader = this.createApplicationClassLoader(config, parentLayers, parentLoader); - final ModuleLayer layer = ModuleLayer.boot().defineModules(config, moduleName -> loader); - - // Run the application - try { - Thread.currentThread().setContextClassLoader(loader); - if (Bootstrap.DEBUG) { - System.out.println("Starting application ..."); - } - this.runApplication(layer); - } finally { - Thread.currentThread().setContextClassLoader(contextLoader); - } - } - - public static String formatUnion(final Path[] paths) { - return Arrays.stream(paths).map(Path::toString).collect(Collectors.joining("&")); - } } diff --git a/bootstrap/src/main/java/org/spongepowered/bootstrap/dev/DevClasspath.java b/bootstrap/src/main/java/org/spongepowered/bootstrap/dev/DevClasspath.java index fc623dffc0e..708594f063a 100644 --- a/bootstrap/src/main/java/org/spongepowered/bootstrap/dev/DevClasspath.java +++ b/bootstrap/src/main/java/org/spongepowered/bootstrap/dev/DevClasspath.java @@ -58,6 +58,7 @@ public static List resolve() { final AtomicBoolean hasAPISourceSet = new AtomicBoolean(false); + boolean skipMixinExtras = false; for (final String str : cp) { final Path path = Paths.get(str); if (!Files.exists(path)) { @@ -79,7 +80,7 @@ public static List resolve() { case "bootstrap": // ignore break; - case "modlauncher-transformers", "library-manager": + case "library-manager": bootUnions.add(projectName, new WeightedPath(0, path)); break; case "SpongeAPI": @@ -96,6 +97,9 @@ public static List resolve() { } break; case "", "vanilla", "forge", "neoforge": + if ("forge".equals(projectName)) { + skipMixinExtras = true; + } final WeightedPath weightedPath = new WeightedPath(projectName.isEmpty() ? 1 : 2, path); switch (sourceSet.name()) { case "applaunchConfig": @@ -132,6 +136,14 @@ public static List resolve() { } continue; } + // We need to skip mixinextras-common in Forge's UserDev mode as this will cause duplicate module + // errors with JPMS. + if (fileName.startsWith("mixinextras-common") && skipMixinExtras) { + if (Bootstrap.DEBUG) { + System.out.println("Ignored: " + path); + } + continue; + } if (bootNames.contains(fileName) || fileName.startsWith("org.jacoco.core-") || fileName.startsWith("mockito-") || fileName.startsWith("objenesis-")) { if (Bootstrap.DEBUG) { @@ -158,7 +170,7 @@ public static List resolve() { final List classpath = new ArrayList<>(); for (final Path lib : bootLibs) { - classpath.add(new Path[] { lib }); + classpath.add(new Path[]{lib}); } for (final List sourceSets : bootUnions.values()) { diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/Constants.java b/bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/DevClient.java similarity index 81% rename from vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/Constants.java rename to bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/DevClient.java index 969dea1eb7c..1895f85d2fb 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/Constants.java +++ b/bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/DevClient.java @@ -22,12 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.applaunch; +package org.spongepowered.bootstrap.neoforge; -public final class Constants { +import net.neoforged.fml.startup.Client; +import org.spongepowered.bootstrap.dev.DevClasspath; - public static final class ManifestAttributes { - public static final String ACCESS_WIDENER = "Access-Widener"; - public static final String SUPERCLASS_CHANGE = "Superclass-Transformer"; +public class DevClient { + + public static void main(final String[] args) { + DevClasspath.resolve(); + Client.main(args); } } diff --git a/bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/DevServer.java b/bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/DevServer.java new file mode 100644 index 00000000000..0eff65b1fb3 --- /dev/null +++ b/bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/DevServer.java @@ -0,0 +1,36 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.bootstrap.neoforge; + +import net.neoforged.fml.startup.Server; +import org.spongepowered.bootstrap.dev.DevClasspath; + +public class DevServer { + + public static void main(final String[] args) { + DevClasspath.resolve(); + Server.main(args); + } +} diff --git a/bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/NeoForgeBootstrap.java b/bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/NeoForgeBootstrap.java deleted file mode 100644 index 84ac0a335ea..00000000000 --- a/bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/NeoForgeBootstrap.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.bootstrap.neoforge; - -import cpw.mods.cl.JarModuleFinder; -import cpw.mods.cl.ModuleClassLoader; -import cpw.mods.jarhandling.SecureJar; -import org.spongepowered.bootstrap.Bootstrap; - -import java.lang.module.Configuration; -import java.lang.module.ModuleFinder; -import java.nio.file.Path; -import java.util.Collection; -import java.util.List; - -public class NeoForgeBootstrap extends Bootstrap { - private final String[] args; - - public NeoForgeBootstrap(final String[] args) { - this.args = args; - } - - @Override - public String name() { - return "NeoForge"; - } - - @Override - protected SecureJar createJar(final Path[] paths) { - return SecureJar.from(paths); - } - - @Override - protected String getModuleName(final SecureJar jar) { - return jar.name(); - } - - @Override - protected ModuleFinder createModuleFinder(final Collection jars) { - return JarModuleFinder.of(jars.toArray(SecureJar[]::new)); - } - - @Override - protected ClassLoader createApplicationClassLoader(final Configuration config, final List parentLayers, final ClassLoader parentLoader) { - return new ModuleClassLoader("APP-BOOTSTRAP", config, parentLayers, parentLoader); - } - - @Override - protected void runApplication(final ModuleLayer layer) throws Exception { - final Class appClass = layer.findModule("cpw.mods.modlauncher").get().getClassLoader().loadClass("cpw.mods.modlauncher.Launcher"); - appClass.getDeclaredMethod("main", String[].class).invoke(null, (Object) this.args); - } - - public static void main(final String[] args) throws Exception { - new NeoForgeBootstrap(args).devBoot(true); - } -} diff --git a/src/main/java/org/spongepowered/common/util/TristateUtil.java b/bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/TestServer.java similarity index 58% rename from src/main/java/org/spongepowered/common/util/TristateUtil.java rename to bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/TestServer.java index eb28df33435..0653b6bc680 100644 --- a/src/main/java/org/spongepowered/common/util/TristateUtil.java +++ b/bootstrap/src/neoforge/java/org/spongepowered/bootstrap/neoforge/TestServer.java @@ -22,30 +22,28 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.util; +package org.spongepowered.bootstrap.neoforge; -import com.google.common.collect.EnumBiMap; -import net.minecraft.world.InteractionResult; -import org.spongepowered.api.util.Tristate; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.fml.startup.Entrypoint; +import net.neoforged.fml.startup.FatalStartupException; +import org.spongepowered.bootstrap.dev.DevClasspath; -public final class TristateUtil { +import java.lang.invoke.MethodHandle; - private static final EnumBiMap map = EnumBiMap.create(InteractionResult.class, Tristate.class); +public class TestServer extends Entrypoint { - static { - TristateUtil.map.put(InteractionResult.FAIL, Tristate.FALSE); - TristateUtil.map.put(InteractionResult.PASS, Tristate.UNDEFINED); - TristateUtil.map.put(InteractionResult.SUCCESS, Tristate.TRUE); - } - - public static Tristate fromActionResult(final InteractionResult result) { - return TristateUtil.map.get(result); - } + public static void main(final String[] args) { + DevClasspath.resolve(); - public static InteractionResult toActionResult(final Tristate tristate) { - return TristateUtil.map.inverse().get(tristate); - } + // Do not close the loader until tests are done + final StartupResult startup = Entrypoint.startup(args, true, Dist.DEDICATED_SERVER, true); - private TristateUtil() { + final MethodHandle main = Entrypoint.createMainMethodCallable(startup, "net.minecraft.server.Main"); + try { + main.invokeExact(startup.loader().getProgramArgs().getArguments()); + } catch (Throwable e) { + throw new FatalStartupException("Failed to invoke main", startup.startupArgs(), e); + } } } diff --git a/build-logic/src/main/java/org/spongepowered/gradle/impl/DigestUtil.java b/build-logic/src/main/java/org/spongepowered/gradle/impl/DigestUtil.java new file mode 100644 index 00000000000..08fe4320f97 --- /dev/null +++ b/build-logic/src/main/java/org/spongepowered/gradle/impl/DigestUtil.java @@ -0,0 +1,87 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.gradle.impl; + +import org.gradle.api.GradleException; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +public class DigestUtil { + private static final char[] hexChars = "0123456789abcdef".toCharArray(); + + public static String toHexString(final byte[] bytes) { + final char[] chars = new char[bytes.length << 1]; + int i = 0; + for (final byte b : bytes) { + chars[i++] = DigestUtil.hexChars[(b >> 4) & 15]; + chars[i++] = DigestUtil.hexChars[b & 15]; + } + return new String(chars); + } + + public static String digest(final Path file, final String algorithm) { + final MessageDigest digest; + try { + digest = MessageDigest.getInstance(algorithm); + } catch (final NoSuchAlgorithmException e) { + throw new GradleException("Failed to find digest algorithm", e); + } + + try (final InputStream in = Files.newInputStream(file)) { + final byte[] buf = new byte[4096]; + int read; + while ((read = in.read(buf)) != -1) { + digest.update(buf, 0, read); + } + } catch (final IOException e) { + throw new GradleException("Failed to digest file " + file, e); + } + + return DigestUtil.toHexString(digest.digest()); + } + + public static void generateChecksums(final File jarFile, final String dirPath, final String algorithm) throws Exception { + try (final FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + jarFile.toURI()), Map.of())) { + final Path dir = fs.getPath(dirPath); + final List lines; + try (final Stream st = Files.list(dir)) { + lines = st.map(p -> DigestUtil.digest(p, algorithm) + " " + p.getFileName()).toList(); + } + Files.write(dir.resolve("checksums.txt"), lines); + } + } +} diff --git a/build-logic/src/main/java/org/spongepowered/gradle/impl/OutputDependenciesToJson.java b/build-logic/src/main/java/org/spongepowered/gradle/impl/OutputDependenciesToJson.java index ae020bb20f3..89824a41893 100644 --- a/build-logic/src/main/java/org/spongepowered/gradle/impl/OutputDependenciesToJson.java +++ b/build-logic/src/main/java/org/spongepowered/gradle/impl/OutputDependenciesToJson.java @@ -41,16 +41,12 @@ import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.Nested; import org.gradle.api.tasks.Optional; -import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; import java.io.BufferedWriter; import java.io.IOException; -import java.io.InputStream; import java.nio.file.Files; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; @@ -62,8 +58,6 @@ public abstract class OutputDependenciesToJson extends DefaultTask { - private static final char[] hexChars = "0123456789abcdef".toCharArray(); - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); /** @@ -172,44 +166,19 @@ private List configToDescriptor(final Set { final ModuleComponentIdentifier id = (ModuleComponentIdentifier) dependency.getId().getComponentIdentifier(); - final MessageDigest digest; + final String digest; try { - digest = MessageDigest.getInstance("SHA-512"); - } catch (final NoSuchAlgorithmException e) { - throw new GradleException("Failed to find digest algorithm", e); - } - - try (final InputStream in = Files.newInputStream(dependency.getFile().toPath())) { - final byte[] buf = new byte[4096]; - int read; - while ((read = in.read(buf)) != -1) { - digest.update(buf, 0, read); - } - } catch (final IOException e) { + digest = DigestUtil.digest(dependency.getFile().toPath(), "SHA-512"); + } catch (final Exception e) { throw new GradleException("Failed to digest file for " + dependency, e); } - return new DependencyDescriptor( - id.getGroup(), - id.getModule(), - id.getVersion(), - OutputDependenciesToJson.toHexString(digest.digest()) - ); + return new DependencyDescriptor(id.getGroup(), id.getModule(), id.getVersion(), digest); }) .sorted(Comparator.naturalOrder()) // sort dependencies for stable output .collect(Collectors.toList()); } - public static String toHexString(final byte[] bytes) { - final char[] chars = new char[bytes.length << 1]; - int i = 0; - for (final byte b : bytes) { - chars[i++] = OutputDependenciesToJson.hexChars[(b >> 4) & 15]; - chars[i++] = OutputDependenciesToJson.hexChars[b & 15]; - } - return new String(chars); - } - public static class ConfigurationHolder { private final Provider> artifacts; diff --git a/build.gradle.kts b/build.gradle.kts index 8cf7ea9be5d..dd2c97407cd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,6 +15,7 @@ plugins { id("implementation-structure") id(apiLibs.plugins.ideaExt.get().pluginId) alias(libs.plugins.versions) + alias(libs.plugins.blossom) } val apiVersion: String by project @@ -67,6 +68,10 @@ val applaunch by sourceSets.registering { configurations.named(implementationConfigurationName) { extendsFrom(serviceLayerConfig.get()) } + + blossom.javaSources { + property("pluginSpiVersion", apiLibs.pluginSpi.get().version) + } } // Game layer @@ -134,10 +139,9 @@ dependencies { testRuntimeOnly(apiLibs.junit.engine) testRuntimeOnly(apiLibs.junit.launcher) - testImplementation(libs.mockito.core) - testImplementation(libs.mockito.junitJupiter) { - exclude(group = "org.junit.jupiter", module = "junit-jupiter-api") - } + testImplementation(apiLibs.mockito) + + testImplementation(libs.mixin) } minecraft { @@ -158,13 +162,21 @@ idea { } } +sourceSets { + test { + blossom.resources { + property("apiVersion", apiVersion.replace("-SNAPSHOT", "")) + } + } +} + allprojects { configurations.configureEach { resolutionStrategy.dependencySubstitution { // https://github.com/zml2008/guice/tree/backport/5.0.1 substitute(module("com.google.inject:guice:5.0.1")) - .because("We need to run against Guava 21") - .using(module("ca.stellardrift.guice-backport:guice:5.0.1")) + .because("We need to run against Guava 21") + .using(module("ca.stellardrift.guice-backport:guice:5.0.1")) } } @@ -244,11 +256,11 @@ allprojects { withType(PublishToMavenRepository::class).configureEach { onlyIf { (repository == publishing.repositories["GitHubPackages"] && - !(rootProject.version as String).endsWith("-SNAPSHOT")) || - (!spongeSnapshotRepo.isNullOrBlank() - && !spongeReleaseRepo.isNullOrBlank() - && repository == publishing.repositories["spongeRepo"] - && publication == publishing.publications["sponge"]) + !(rootProject.version as String).endsWith("-SNAPSHOT")) || + (!spongeSnapshotRepo.isNullOrBlank() + && !spongeReleaseRepo.isNullOrBlank() + && repository == publishing.repositories["spongeRepo"] + && publication == publishing.publications["sponge"]) } } diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts index b4690b78ec1..bc7f9e01172 100644 --- a/forge/build.gradle.kts +++ b/forge/build.gradle.kts @@ -1,8 +1,5 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import net.minecraftforge.gradle.common.util.RunConfig -import org.gradle.internal.DefaultTaskExecutionRequest import org.spongepowered.gradle.impl.AWToAT -import org.spongepowered.gradle.impl.IdeHelper buildscript { repositories { @@ -25,7 +22,6 @@ plugins { val commonProject = parent!! val bootstrapProject = commonProject.project(":bootstrap") -val transformersProject = commonProject.project(":modlauncher-transformers") val libraryManagerProject = commonProject.project(":library-manager") val testPluginsProject: Project? = rootProject.subprojects.find { "testplugins" == it.name } @@ -38,17 +34,23 @@ val projectUrl: String by project description = "The SpongeAPI implementation for MinecraftForge" version = spongeImpl.generatePlatformBuildVersionString(apiVersion, minecraftVersion, recommendedVersion, forgeVersion) +val fg = the() + repositories { + minecraft.mavenizer(this) + maven(fg.forgeMaven) + maven(fg.minecraftLibsMaven) maven("https://repo.spongepowered.org/repository/maven-public/") { name = "sponge" } } +// Dedicated configuration for the Forge/Minecraft dependency (replaces old configurations.minecraft from FG 6.x) +val minecraftLibsConfig = configurations.register("minecraftLibs") + // SpongeForge libraries val bootLibrariesConfig = configurations.register("bootLibraries") { - // Ideally we would filter minecraft itself and only keep its dependencies for this layer, - // but I couldn't find a way to do it without breaking ForgeGradle. - extendsFrom(configurations.minecraft.get()) + extendsFrom(minecraftLibsConfig.get()) } val serviceLibrariesConfig = configurations.register("serviceLibraries") val gameLibrariesConfig = configurations.register("gameLibraries") @@ -60,6 +62,8 @@ val gameShadedLibrariesConfig = configurations.register("gameShadedLibraries") val productionExcludedLibrariesConfig = configurations.register("productionExcludedLibraries") +val testModulesConfig = configurations.register("testModules") + // ModLauncher layers val bootLayerConfig = configurations.register("bootLayer") { extendsFrom(bootLibrariesConfig.get()) @@ -156,6 +160,10 @@ val testSources = sourceSets.named("test") { spongeImpl.addDependencyToImplementation(bootstrapMain.get(), this) spongeImpl.addDependencyToImplementation(bootstrapForge.get(), this) + + configurations.named(implementationConfigurationName) { + extendsFrom(testModulesConfig.get()) + } } configurations.configureEach { @@ -169,14 +177,15 @@ configurations.testRuntimeOnly { exclude(module = "testplugins") } +val forgeDep = minecraft.dependency("net.minecraftforge:forge:$minecraftVersion-$forgeVersion") + dependencies { - "minecraft"("net.minecraftforge:forge:$minecraftVersion-$forgeVersion") + implementation(forgeDep) + minecraftLibsConfig.name(forgeDep) val service = serviceLibrariesConfig.name service(apiLibs.pluginSpi) - service(project(transformersProject.path)) { - exclude(group = "cpw.mods", module = "modlauncher") - } + service(libs.accessWidener) service(project(libraryManagerProject.path)) val game = gameLibrariesConfig.name @@ -185,9 +194,9 @@ dependencies { game(platform(apiLibs.adventure.bom)) game(libs.adventure.serializerConfigurate4) game(libs.mixinextras.common) + game(libs.mixinextras.forge) val serviceShadedLibraries = serviceShadedLibrariesConfig.name - serviceShadedLibraries(project(transformersProject.path)) { isTransitive = false } serviceShadedLibraries(project(libraryManagerProject.path)) { isTransitive = false } val gameShadedLibraries = gameShadedLibrariesConfig.name @@ -214,10 +223,8 @@ dependencies { testImplementation(apiLibs.junit.launcher) testRuntimeOnly(apiLibs.junit.engine) - testImplementation(libs.mockito.core) - testImplementation(libs.mockito.junitJupiter) { - exclude(group = "org.junit.jupiter", module = "junit-jupiter-api") - } + val test = testModulesConfig.name + test(apiLibs.mockito) testRuntimeOnly(libs.jacoco.core) { exclude(group = "org.ow2.asm") @@ -230,25 +237,32 @@ AWToAT.convert(awFiles, atFile) val mixinConfigs: MutableSet = spongeImpl.mixinConfigurations +// FG resolves its tools on exactly the Java version they target (SlimeLauncher: 8) unless overridden; any newer JDK runs it fine +fgtools.configure("slimelauncher") { + javaLauncher.set(javaToolchains.launcherFor(java.toolchain)) +} + minecraft { - mappings("official", "1.21.1") + mappings("official", minecraftVersion) accessTransformers.from(atFile) - reobf = false runs { configureEach { - ideaModule("Sponge.SpongeForge.main") - // jvmArgs("-Dsponge.bootstrap.debug=true") // Uncomment to debug bootstrap classpath - main("org.spongepowered.bootstrap.forge.ForgeBootstrap") + // jvmArgs("-Dmixin.debug=true", "-Dmixin.debug.export=true", "-Dmixin.dumpTargetOnFailure=true") + mainClass.set("org.spongepowered.bootstrap.forge.ForgeBootstrap") args(mixinConfigs.flatMap { sequenceOf("--mixin.config", it) }) environment("MOD_CLASSES", "nop") } - create("client") + register("client") { + if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) { + jvmArgs("-XstartOnFirstThread") + } + } - create("server") { + register("server") { args("--nogui") } } @@ -259,22 +273,24 @@ afterEvaluate { // Configure bootstrap dev val bootFileNames = spongeImpl.buildRuntimeFileNames(serviceLayerConfig.get()) // service in boot during dev val gameShadedFileNames = spongeImpl.buildRuntimeFileNames(gameShadedLibrariesConfig.get()) - runs.configureEach { - jvmArgs("-Dsponge.dev.root=" + project.rootDir) - jvmArgs("-Dsponge.dev.boot=$bootFileNames") - jvmArgs("-Dsponge.dev.gameShaded=$gameShadedFileNames") + runs { + configureEach { + jvmArgs("-Dsponge.dev.root=" + project.rootDir) + jvmArgs("-Dsponge.dev.boot=$bootFileNames") + jvmArgs("-Dsponge.dev.gameShaded=$gameShadedFileNames") + } } } } val forgeManifest = java.manifest { attributes( - "Specification-Title" to "SpongeForge", - "Specification-Vendor" to "SpongePowered", - "Specification-Version" to apiVersion, - "Implementation-Title" to project.name, - "Implementation-Version" to version, - "Implementation-Vendor" to "SpongePowered" + "Specification-Title" to "SpongeForge", + "Specification-Vendor" to "SpongePowered", + "Specification-Version" to apiVersion, + "Implementation-Title" to project.name, + "Implementation-Version" to version, + "Implementation-Vendor" to "SpongePowered" ) // These two are included by most CI's System.getenv()["GIT_COMMIT"]?.apply { attributes("Git-Commit" to this) } @@ -306,7 +322,7 @@ sourceSets { tasks { withType(JavaExec::class) { - if (group == RunConfig.RUNS_GROUP) { + if (group == "Slime Launcher") { standardInput = System.`in` } } @@ -415,9 +431,14 @@ tasks { testClassesDirs = commonTest.get().output.classesDirs + testSources.get().output.classesDirs val runServer = minecraft.runs.getByName("server") - jvmArgs(runServer.jvmArgs) - jvmArgs("-Dsponge.test.args=" + runServer.args.joinToString(" ")) - jvmArgs("-Dsponge.jacoco.packages=org.spongepowered") + jvmArgs(runServer.jvmArgs.get()) + jvmArgs("--module-path=" + testModulesConfig.get().asPath) + jvmArgs("--add-modules=net.bytebuddy.agent,net.bytebuddy") + jvmArgs("-Dsponge.test.args=--launchTarget forge_userdev_server " + runServer.args.get().joinToString(" ")) + // Don't set sponge.jacoco.packages — the JaCoCo agent is disabled + // and Sponge's JacocoTransformer would inject Offline references + // that can't be resolved without the agent runtime. + // jvmArgs("-Dsponge.jacoco.packages=org.spongepowered") jvmArgs("-Djunit.platform.launcher.interceptors.enabled=true") jvmArgs("-Djunit.jupiter.extensions.autodetection.enabled=true") workingDir = layout.buildDirectory.dir("test-run").get().asFile @@ -443,11 +464,7 @@ tasks { } } -if (IdeHelper.isIdeaSync()) { - afterEvaluate { - gradle.startParameter.taskRequests.add(DefaultTaskExecutionRequest(listOf(":SpongeForge:genIntellijRuns"))) - } -} + publishing { publications { diff --git a/forge/gradle.properties b/forge/gradle.properties index 50bcd159c70..eacd81e315c 100644 --- a/forge/gradle.properties +++ b/forge/gradle.properties @@ -2,5 +2,5 @@ name=SpongeForge implementation=Forge description=The SpongeAPI implementation for MinecraftForge -forgeVersion=52.1.5 +forgeVersion=65.1.1 mixinConfigs=mixins.spongeforge.accessors.json,mixins.spongeforge.api.json,mixins.spongeforge.inventory.json,mixins.spongeforge.inventory.shared.json,mixins.spongeforge.core.json,mixins.spongeforge.core.shared.json,mixins.spongeforge.tracker.json diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeModLocator.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeModLocator.java deleted file mode 100644 index 841f97069a7..00000000000 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeModLocator.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.applaunch.loading.moddiscovery; - -import com.google.common.collect.ImmutableMap; -import cpw.mods.modlauncher.Environment; -import cpw.mods.modlauncher.Launcher; -import net.minecraftforge.fml.loading.FMLEnvironment; -import net.minecraftforge.forgespi.locating.IModLocator; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.spongepowered.forge.applaunch.plugin.ForgePluginPlatform; - -import java.io.File; -import java.net.URI; -import java.net.URL; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.stream.Stream; - -public final class SpongeForgeModLocator extends AbstractModProvider implements IModLocator { - private static final Logger LOGGER = LogManager.getLogger(); - - @Override - public List scanMods() { - if (!FMLEnvironment.production) { - final List resources = new ArrayList<>(); - final String resourcesProp = System.getProperty("sponge.resources"); - if (resourcesProp != null) { - for (final String entry : resourcesProp.split(File.pathSeparator)) { - if (entry.isBlank()) { - continue; - } - - final Path[] paths = Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new); - resources.add(new ModFileOrException(PluginFileParser.newModFile(this, true, paths), null)); - } - } - return resources; - } - - try { - URL rootJar = SpongeForgeModLocator.class.getProtectionDomain().getCodeSource().getLocation(); - FileSystem fs = FileSystems.getFileSystem(rootJar.toURI()); // FML has already opened a file system for this jar - return Files.list(fs.getPath("jars")) - .filter(path -> path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".jar")) - .map(path -> { - try { - URI jij = new URI("jij:" + path.toAbsolutePath().toUri().getRawSchemeSpecificPart()).normalize(); - final Map env = ImmutableMap.of("packagePath", path); - FileSystem jijFS = FileSystems.newFileSystem(jij, env); - return jijFS.getPath("/"); // root of the archive to load - } catch (Exception e) { - throw new RuntimeException(e); - } - }).map((path -> new ModFileOrException(PluginFileParser.newModFile(this, false, path), null))).toList(); - } catch (Exception e) { - LOGGER.error("Failed to scan mod candidates", e); - } - - return List.of(); - } - - @Override - public String name() { - return "spongeforge"; - } - - @Override - public void initArguments(Map arguments) { - final Environment env = Launcher.INSTANCE.environment(); - ForgePluginPlatform.bootstrap(env); - } -} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/EnvironmentPluginLocator.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/EnvironmentPluginLocator.java deleted file mode 100644 index a76911d39dd..00000000000 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/EnvironmentPluginLocator.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.applaunch.loading.moddiscovery.locator; - -import net.minecraftforge.forgespi.locating.IModLocator; -import org.spongepowered.forge.applaunch.loading.moddiscovery.AbstractModProvider; -import org.spongepowered.forge.applaunch.loading.moddiscovery.PluginFileParser; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Stream; - -public final class EnvironmentPluginLocator extends AbstractModProvider implements IModLocator { - - @Override - public List scanMods() { - final List modFiles = new ArrayList<>(); - for (final Path[] paths : EnvironmentPluginLocator.getPluginsPaths()) { - modFiles.add(new ModFileOrException(PluginFileParser.newModFile(this, false, paths), null)); - } - return modFiles; - } - - @Override - public String name() { - return "environment plugin"; - } - - private static List getPluginsPaths() { - final String env = System.getenv("SPONGE_PLUGINS"); - if (env == null) { - return Collections.emptyList(); - } - - List plugins = new ArrayList<>(); - for (final String entry : env.split(";")) { - if (entry.isBlank()) { - continue; - } - plugins.add(Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new)); - } - - return plugins; - } -} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/PluginsDirectoryLocator.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/PluginsDirectoryLocator.java deleted file mode 100644 index e0ed9845745..00000000000 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/locator/PluginsDirectoryLocator.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.applaunch.loading.moddiscovery.locator; - -import cpw.mods.modlauncher.api.LamdbaExceptionUtils; -import net.minecraftforge.fml.loading.ModDirTransformerDiscoverer; -import net.minecraftforge.fml.loading.StringUtils; -import net.minecraftforge.fml.loading.moddiscovery.ModFile; -import net.minecraftforge.forgespi.locating.IModLocator; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.forge.applaunch.loading.moddiscovery.AbstractModProvider; -import org.spongepowered.forge.applaunch.loading.moddiscovery.PluginFileParser; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Stream; - -public final class PluginsDirectoryLocator extends AbstractModProvider implements IModLocator { - private static final Logger LOGGER = LogManager.getLogger(); - - @Override - public List scanMods() { - final List pluginDirectories = AppLaunch.pluginPlatform().pluginDirectories(); - - final List modFiles = new ArrayList<>(); - - for (final Path pluginDirectory : pluginDirectories) { - PluginsDirectoryLocator.LOGGER.debug("Scanning plugins directory '{}' for plugins", pluginDirectory); - this.scanForPluginsIn(pluginDirectory).map((f) -> new ModFileOrException(f, null)).forEach(modFiles::add); - } - - return modFiles; - } - - @SuppressWarnings("UnstableApiUsage") - private Stream scanForPluginsIn(final Path pluginsDirectory) { - final List excluded = ModDirTransformerDiscoverer.allExcluded(); - return LamdbaExceptionUtils.uncheck(() -> Files.list(pluginsDirectory)) - .filter((p) -> !excluded.contains(p) && StringUtils.toLowerCase(p.getFileName().toString()).endsWith(".jar")) - .sorted(Comparator.comparing((path) -> StringUtils.toLowerCase(path.getFileName().toString()))) - .map((p) -> PluginFileParser.newModFile(this, false, p)) - .filter(ModFile::identifyMods); - } - - @Override - public String name() { - return "plugins directory"; - } -} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/AbstractModProvider.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/AbstractModProvider.java similarity index 98% rename from forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/AbstractModProvider.java rename to forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/AbstractModProvider.java index 5b71dde2099..7836e9ea878 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/AbstractModProvider.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/AbstractModProvider.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.applaunch.loading.moddiscovery; +package org.spongepowered.forge.applaunch.mod.discovery; import cpw.mods.jarhandling.SecureJar; import net.minecraftforge.forgespi.locating.IModFile; diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/PluginFileParser.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/PluginFileParser.java similarity index 61% rename from forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/PluginFileParser.java rename to forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/PluginFileParser.java index f63dfb7c498..21781a36955 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/PluginFileParser.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/PluginFileParser.java @@ -22,9 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.applaunch.loading.moddiscovery; +package org.spongepowered.forge.applaunch.mod.discovery; -import cpw.mods.jarhandling.JarMetadata; import cpw.mods.jarhandling.SecureJar; import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider; import net.minecraftforge.fml.loading.moddiscovery.ModFile; @@ -34,17 +33,13 @@ import net.minecraftforge.forgespi.language.IModFileInfo; import net.minecraftforge.forgespi.locating.IModFile; import net.minecraftforge.forgespi.locating.IModProvider; -import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.common.applaunch.metadata.PluginMetadataFixer; -import org.spongepowered.common.applaunch.plugin.PluginPlatformConstants; -import org.spongepowered.forge.applaunch.loading.metadata.PluginFileConfigurable; -import org.spongepowered.plugin.metadata.builtin.MetadataContainer; -import org.spongepowered.plugin.metadata.builtin.MetadataParser; - -import java.io.Reader; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.forge.applaunch.mod.metadata.PluginFileConfigurable; +import org.spongepowered.forge.applaunch.plugin.discovery.PluginJarMetadata; +import org.spongepowered.forge.applaunch.plugin.discovery.SecureJarPluginResource; + import java.lang.reflect.Constructor; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -64,36 +59,16 @@ public final class PluginFileParser { } } - private static IModFileInfo parsePluginFileInfo(final IModFile iModFile) { - final ModFile modFile = (ModFile) iModFile; - AppLaunch.logger().debug("Considering plugin file candidate {}", modFile.getFilePath()); - - final Path metadataFile = modFile.findResource(PluginPlatformConstants.METADATA_FILE_LOCATION); - if (Files.notExists(metadataFile)) { - AppLaunch.logger().debug("Plugin file '{}' is missing a 'sponge_plugins.json' metadata file in META-INF", modFile); - return null; - } - - try { - final MetadataContainer container; - try (final Reader reader = Files.newBufferedReader(metadataFile, StandardCharsets.UTF_8)) { - container = MetadataParser.read(reader); - } - - final PluginFileConfigurable config = new PluginFileConfigurable(PluginMetadataFixer.fixPluginIds(container)); - return new ModFileInfo(modFile, config, (info) -> {}, List.of()); - } catch (final Exception e) { - AppLaunch.logger().warn("Could not read metadata for plugin file '{}'", modFile, e); - return null; - } + private static IModFileInfo parsePluginFileInfo(final IModFile file, final PluginFileConfigurable config) { + return new ModFileInfo((ModFile) file, config, (info) -> {}, List.of()); } - private static IModFileInfo parseModFileInfo(final IModFile iModFile) { - return ModFileParser.modsTomlParser(iModFile); + private static IModFileInfo parseModFileInfo(final IModFile file) { + return ModFileParser.modsTomlParser(file); } - private static IModFileInfo parseLibraryFileInfo(final IModFile iModFile) { - return DummyModProvider.INSTANCE.manifestParser(iModFile); + private static IModFileInfo parseLibraryFileInfo(final IModFile file) { + return DummyModProvider.INSTANCE.manifestParser(file); } private static ModJarMetadata newModJarMetadata() { @@ -109,13 +84,31 @@ private static boolean useModJarMetadata(final SecureJar jar) { if (data.findFile(PluginFileParser.MODULE_INFO).isPresent()) { return false; } - return data.findFile(PluginFileParser.MODS_TOML).isPresent() || data.findFile(PluginPlatformConstants.METADATA_FILE_LOCATION).isPresent(); + return data.findFile(PluginFileParser.MODS_TOML).isPresent(); + } + + public static ModFile newJarPluginFile(final IModProvider provider, final PluginDiscovery.Candidate candidate) { + final SecureJarPluginResource resource = (SecureJarPluginResource) candidate.resource(); + return PluginFileParser.newPluginFile(provider, candidate, resource.pluginJar(candidate.metadata().getFirst())); + } + + public static ModFile newNonJarPluginFile(final IModProvider provider, final PluginDiscovery.Candidate candidate, final Path tempPath) { + final SecureJar jar = SecureJar.from(j -> new PluginJarMetadata(j, candidate.metadata().getFirst()), tempPath); + return PluginFileParser.newPluginFile(provider, candidate, jar); } - public static ModFile newModFile(final IModProvider provider, boolean allowUnknown, final Path... paths) { + private static ModFile newPluginFile(final IModProvider provider, final PluginDiscovery.Candidate candidate, final SecureJar jar) { + final PluginFileConfigurable config = new PluginFileConfigurable(candidate.resource(), candidate.metadata()); + return new ModFile(jar, provider, file -> parsePluginFileInfo(file, config), "MOD"); + } + + public static @Nullable ModFile newModFile(final IModProvider provider, SecureJarPluginResource resource) { final ModJarMetadata mjm = newModJarMetadata(); - final SecureJar jar = SecureJar.from(j -> useModJarMetadata(j) ? mjm : JarMetadata.from(j, paths), paths); + final SecureJar jar = PluginFileParser.useModJarMetadata(resource.jar()) ? resource.modJar(mjm) : resource.jar(); + return PluginFileParser.newModFile(provider, mjm, jar); + } + private static @Nullable ModFile newModFile(final IModProvider provider, final ModJarMetadata mjm, final SecureJar jar) { final SecureJar.ModuleDataProvider data = jar.moduleDataProvider(); final String type = data.getManifest().getMainAttributes().getValue("FMLModType"); @@ -125,21 +118,14 @@ public static ModFile newModFile(final IModProvider provider, boolean allowUnkno return modFile; } - if (data.findFile(PluginPlatformConstants.METADATA_FILE_LOCATION).isPresent()) { - ModFile modFile = new ModFile(jar, provider, PluginFileParser::parsePluginFileInfo, type == null ? "MOD" : type); - mjm.setModFile(modFile); - return modFile; - } - - if (!allowUnknown && type == null) { - throw new IllegalArgumentException("Unknown mod file type"); + if (type == null) { + return null; } - return new ModFile(jar, provider, PluginFileParser::parseLibraryFileInfo, type == null ? "GAMELIBRARY" : type); + return new ModFile(jar, provider, PluginFileParser::parseLibraryFileInfo, type); } - public static ModFile newLibraryFile(final IModProvider provider, final Path... paths) { - final SecureJar jar = SecureJar.from(paths); + public static ModFile newGameLibraryFile(final IModProvider provider, final SecureJar jar) { return new ModFile(jar, provider, PluginFileParser::parseLibraryFileInfo, "GAMELIBRARY"); } diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeDependencyLocator.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/SpongeForgeDependencyLocator.java similarity index 76% rename from forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeDependencyLocator.java rename to forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/SpongeForgeDependencyLocator.java index c063b7ae7a6..37656bf37ea 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/SpongeForgeDependencyLocator.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/SpongeForgeDependencyLocator.java @@ -22,8 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.applaunch.loading.moddiscovery; +package org.spongepowered.forge.applaunch.mod.discovery; +import cpw.mods.jarhandling.SecureJar; import net.minecraftforge.fml.loading.FMLEnvironment; import net.minecraftforge.forgespi.locating.IDependencyLocator; import net.minecraftforge.forgespi.locating.IModFile; @@ -31,12 +32,16 @@ import org.apache.logging.log4j.Logger; import org.spongepowered.common.applaunch.AppLaunch; import org.spongepowered.common.applaunch.plugin.PluginPlatform; -import org.spongepowered.forge.applaunch.loading.moddiscovery.library.Log4JLogger; +import org.spongepowered.forge.applaunch.mod.discovery.library.Log4JLogger; +import org.spongepowered.forge.applaunch.plugin.metadata.PluginMetadataConverter; import org.spongepowered.libs.LibraryManager; +import org.spongepowered.plugin.metadata.PluginMetadata; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; public class SpongeForgeDependencyLocator extends AbstractModProvider implements IDependencyLocator { private static final Logger LOGGER = LogManager.getLogger(); @@ -45,6 +50,14 @@ public class SpongeForgeDependencyLocator extends AbstractModProvider implements public List scanMods(final Iterable loadedMods) { final List modFiles = new ArrayList<>(); + final Stream allMetadata = StreamSupport.stream(loadedMods.spliterator(), false) + .flatMap(file -> file.getModInfos().stream()) + .map(info -> info.getConfig().getConfigElement("spongeMetadata").orElseGet(() -> PluginMetadataConverter.modToPlugin(info))); + + if (AppLaunch.pluginPlatform().discovery().checkConflicts(allMetadata)) { + throw new IllegalStateException("Fatal plugin conflicts have been detected."); + } + // Add Sponge-specific libraries if (FMLEnvironment.production) { final PluginPlatform platform = AppLaunch.pluginPlatform(); @@ -65,7 +78,7 @@ public List scanMods(final Iterable loadedMods) { for (final LibraryManager.Library library : libraryManager.getAll("main")) { final Path path = library.file(); SpongeForgeDependencyLocator.LOGGER.debug("Proposing jar {} as a game library", path); - modFiles.add(PluginFileParser.newLibraryFile(this, path)); + modFiles.add(PluginFileParser.newGameLibraryFile(this, SecureJar.from(path))); } } diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/SpongeForgeModLocator.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/SpongeForgeModLocator.java new file mode 100644 index 00000000000..0419cd5ab7b --- /dev/null +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/SpongeForgeModLocator.java @@ -0,0 +1,125 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.applaunch.mod.discovery; + +import net.minecraftforge.forgespi.locating.IModFile; +import net.minecraftforge.forgespi.locating.IModLocator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.spongepowered.common.applaunch.AppLaunch; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.forge.applaunch.plugin.ForgePluginPlatform; +import org.spongepowered.forge.applaunch.plugin.discovery.ForgePluginDiscovery; +import org.spongepowered.forge.applaunch.plugin.discovery.SecureJarPluginResource; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.jar.JarOutputStream; + +public final class SpongeForgeModLocator extends AbstractModProvider implements IModLocator { + private static final Logger LOGGER = LogManager.getLogger(); + + private @MonotonicNonNull ForgePluginDiscovery discovery; + + @Override + public String name() { + return "spongeforge"; + } + + @Override + public void initArguments(Map arguments) { + this.discovery = AppLaunch.bootstrap(ForgePluginPlatform::new).discovery(); + } + + @Override + public List scanMods() { + this.discovery.discoverPluginResources(); + + final List mods = new ArrayList<>(); + + Path virtualDir = null; + int virtualJarCount = 0; + + for (final PluginDiscovery.Candidate candidate : this.discovery.candidates()) { + IModFile modFile; + if (candidate.resource() instanceof SecureJarPluginResource resource) { + candidate.readMetadata(); + + // attempt to load as mod or plugin + if (candidate.pluginFound()) { + modFile = PluginFileParser.newJarPluginFile(this, candidate); + } else { + modFile = PluginFileParser.newModFile(this, resource); + if (modFile != null) { + candidate.setModFound(); + } + } + + if (modFile == null) { + // fallback + if (!candidate.gameResource()) { + candidate.logResult(); + continue; + } + modFile = PluginFileParser.newGameLibraryFile(this, resource.jar()); + } + } else { + candidate.readMetadata(); + if (!candidate.pluginFound()) { + candidate.logResult(); + continue; + } + + // Forge requires each mod to have an existing file, so we generate one + final Path path; + try { + if (virtualDir == null) { + virtualDir = Files.createTempDirectory("sponge_resources_"); + } + path = virtualDir.resolve(virtualJarCount++ + ".jar"); + + // an empty jar is enough + new JarOutputStream(Files.newOutputStream(path)).close(); + } catch (IOException e) { + LOGGER.error("Failed to create virtual jar for resource {}", candidate.resource(), e); + continue; + } + + LOGGER.debug("Using temporary jar {} for resource {}", path, candidate.resource()); + modFile = PluginFileParser.newNonJarPluginFile(this, candidate, path); + } + + mods.add(new ModFileOrException(modFile, null)); + candidate.logResult(); + } + + this.discovery.logMetadataWarnings(); + + return mods; + } +} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/library/Log4JLogger.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/library/Log4JLogger.java similarity index 96% rename from forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/library/Log4JLogger.java rename to forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/library/Log4JLogger.java index 31f7369862f..ab372157887 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/moddiscovery/library/Log4JLogger.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/discovery/library/Log4JLogger.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.applaunch.loading.moddiscovery.library; +package org.spongepowered.forge.applaunch.mod.discovery.library; import org.spongepowered.libs.Logger; diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginDependencyConfigurable.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/metadata/PluginDependencyConfigurable.java similarity index 84% rename from forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginDependencyConfigurable.java rename to forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/metadata/PluginDependencyConfigurable.java index 5ddd35637a4..30e592d2506 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginDependencyConfigurable.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/metadata/PluginDependencyConfigurable.java @@ -22,11 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.applaunch.loading.metadata; +package org.spongepowered.forge.applaunch.mod.metadata; import net.minecraftforge.forgespi.language.IConfigurable; import net.minecraftforge.forgespi.language.IModInfo; -import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.plugin.metadata.model.PluginDependency; import java.util.Collections; @@ -36,11 +35,9 @@ // ModVersion public final class PluginDependencyConfigurable implements IConfigurable { - private final PluginMetadata metadata; private final PluginDependency dependency; - public PluginDependencyConfigurable(final PluginMetadata metadata, final PluginDependency dependency) { - this.metadata = metadata; + public PluginDependencyConfigurable(final PluginDependency dependency) { this.dependency = dependency; } @@ -55,7 +52,7 @@ public Optional getConfigElement(final String... key) { case "modId" -> (Optional) Optional.of(this.dependency.id()); case "mandatory" -> (Optional) Optional.of(!this.dependency.optional()); case "versionRange" -> (Optional) Optional.of(this.dependency.version().toString()); - case "ordering" -> (Optional) Optional.of(this.loadToOrdering(this.dependency.loadOrder()).toString()); + case "ordering" -> (Optional) Optional.of(PluginDependencyConfigurable.loadToOrdering(this.dependency.loadOrder()).toString()); case "side" -> (Optional) Optional.of(IModInfo.DependencySide.BOTH.toString()); default -> Optional.empty(); }; @@ -66,7 +63,7 @@ public List getConfigList(final String... key) { return Collections.emptyList(); } - private IModInfo.Ordering loadToOrdering(final PluginDependency.LoadOrder order) { + private static IModInfo.Ordering loadToOrdering(final PluginDependency.LoadOrder order) { return switch (order) { case UNDEFINED -> IModInfo.Ordering.NONE; case BEFORE -> IModInfo.Ordering.BEFORE; diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginFileConfigurable.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/metadata/PluginFileConfigurable.java similarity index 61% rename from forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginFileConfigurable.java rename to forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/metadata/PluginFileConfigurable.java index ee1c28f4e0a..4b5e39fcff0 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginFileConfigurable.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/metadata/PluginFileConfigurable.java @@ -22,26 +22,25 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.applaunch.loading.metadata; +package org.spongepowered.forge.applaunch.mod.metadata; import net.minecraftforge.forgespi.language.IConfigurable; +import org.spongepowered.plugin.discovery.PluginResource; import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.MetadataContainer; -import org.spongepowered.plugin.metadata.model.PluginDependency; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; +import java.util.stream.Collectors; // ModFileInfo public final class PluginFileConfigurable implements IConfigurable { + private final Map plugins = new LinkedHashMap<>(); + private final PluginResource resource; - private final MetadataContainer container; - - public PluginFileConfigurable(final MetadataContainer container) { - this.container = container; + public PluginFileConfigurable(final PluginResource resource, final Collection plugins) { + for (final PluginMetadata plugin : plugins) { + this.plugins.put(plugin.id(), plugin); + } + this.resource = resource; } @SuppressWarnings("unchecked") @@ -56,27 +55,33 @@ public Optional getConfigElement(final String... key) { return Optional.empty(); } + if ("spongeResource".equals(query)) { + return Optional.of((T) this.resource); + } + if ("modLoader".equals(query)) { - return (Optional) Optional.of(this.container.loader().name()); + return Optional.of((T) "sponge"); } if ("loaderVersion".equals(query)) { - return (Optional) Optional.of(this.container.loader().version().toString()); + return Optional.of((T) "*"); } if ("license".equals(query)) { - return (Optional) Optional.of(this.container.license()); + final String joinedLicenses = this.plugins.values().stream() + .flatMap(m -> m.license().stream()).distinct().collect(Collectors.joining(", ")); + return Optional.of((T) (joinedLicenses.isEmpty() ? "?" : joinedLicenses)); } if (key.length == 2) { final String plugin = key[1]; - final PluginMetadata metadata = this.container.metadata(plugin).orElse(null); + final PluginMetadata metadata = this.plugins.get(plugin); if (metadata == null) { return Optional.empty(); } if ("modproperties".equals(query)) { - return (Optional) Optional.of(metadata.properties()); + return Optional.of((T) metadata.properties()); } } @@ -95,38 +100,21 @@ public List getConfigList(final String... key) { } if ("mods".equals(query)) { - final Set metadataById = this.container.metadata(); - if (metadataById.isEmpty()) { - return Collections.emptyList(); - } - - final List metadataConfigurables = new ArrayList<>(); - metadataById.forEach((metadata) -> metadataConfigurables.add(new PluginMetadataConfigurable(metadata))); - return metadataConfigurables; + return this.plugins.values().stream().map(PluginMetadataConfigurable::new).toList(); } if (key.length != 2) { return Collections.emptyList(); } - final String plugin = key[1]; - final PluginMetadata metadata = this.container.metadata(plugin).orElse(null); + final String pluginId = key[1]; + final PluginMetadata metadata = this.plugins.get(pluginId); if (metadata == null) { return Collections.emptyList(); } if ("dependencies".equals(query)) { - final Set dependencies = metadata.dependencies(); - if (dependencies.isEmpty()) { - return Collections.emptyList(); - } - - final List depConfigurables = new ArrayList<>(); - for (final PluginDependency dependency : dependencies) { - depConfigurables.add(new PluginDependencyConfigurable(metadata, dependency)); - } - - return depConfigurables; + return metadata.dependencies().stream().map(PluginDependencyConfigurable::new).toList(); } return Collections.emptyList(); @@ -136,7 +124,6 @@ private int requiredConfigElements(final String query) { if ("dependencies".equals(query) || "modproperties".equals(query)) { return 2; } - return 1; } } diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginMetadataConfigurable.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/metadata/PluginMetadataConfigurable.java similarity index 88% rename from forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginMetadataConfigurable.java rename to forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/metadata/PluginMetadataConfigurable.java index 95584102d07..50b2371082e 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/loading/metadata/PluginMetadataConfigurable.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/mod/metadata/PluginMetadataConfigurable.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.applaunch.loading.metadata; +package org.spongepowered.forge.applaunch.mod.metadata; import net.minecraftforge.forgespi.language.IConfigurable; import org.spongepowered.plugin.metadata.PluginMetadata; @@ -32,13 +32,7 @@ import java.util.Optional; // ModInfo -public final class PluginMetadataConfigurable implements IConfigurable { - - private final PluginMetadata metadata; - - public PluginMetadataConfigurable(final PluginMetadata metadata) { - this.metadata = metadata; - } +public record PluginMetadataConfigurable(PluginMetadata metadata) implements IConfigurable { @SuppressWarnings("unchecked") @Override @@ -53,6 +47,7 @@ public Optional getConfigElement(final String... key) { case "displayName" -> (Optional) this.metadata.name(); case "description" -> (Optional) this.metadata.description(); case "displayTest" -> Optional.of((T) "IGNORE_SERVER_VERSION"); + case "spongeMetadata" -> Optional.of((T) this.metadata); default -> Optional.empty(); }; } diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/ForgePluginPlatform.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/ForgePluginPlatform.java index fb545091686..0c9c987ce6a 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/ForgePluginPlatform.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/ForgePluginPlatform.java @@ -24,68 +24,23 @@ */ package org.spongepowered.forge.applaunch.plugin; -import cpw.mods.modlauncher.Environment; -import cpw.mods.modlauncher.api.IEnvironment; import net.minecraftforge.fml.loading.FMLPaths; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.common.applaunch.config.LaunchConfig; -import org.spongepowered.common.applaunch.config.TokenReplacement; import org.spongepowered.common.applaunch.plugin.PluginPlatform; +import org.spongepowered.forge.applaunch.plugin.discovery.ForgePluginDiscovery; +import org.spongepowered.forge.applaunch.plugin.discovery.SecureJarPluginResource; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; -import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; -public final class ForgePluginPlatform implements PluginPlatform { +public final class ForgePluginPlatform extends PluginPlatform { + private static final List closeCallbacks = new ArrayList<>(); - private static volatile boolean bootstrapped; + private final ForgePluginDiscovery discovery; - private final Environment environment; - private final Logger logger; - private final LaunchConfig config; - private final TokenReplacement tokens; - private final List pluginDirectories; - - public static synchronized void bootstrap(final Environment environment) { - if (ForgePluginPlatform.bootstrapped) { - return; - } - ForgePluginPlatform.bootstrapped = true; - final ForgePluginPlatform platform; - try { - platform = new ForgePluginPlatform(environment); - } catch (final IOException e) { - throw new RuntimeException(e); - } - AppLaunch.setPluginPlatform(platform); - } - - private ForgePluginPlatform(final Environment environment) throws IOException { - this.environment = environment; - this.logger = LogManager.getLogger("plugin"); - this.config = LaunchConfig.load(this.baseDirectory(), true); - - this.tokens = new TokenReplacement(); - this.tokens.register("BASE_DIR", this.baseDirectory()); - this.tokens.register("CONFIG_DIR", this.configDirectory()); - this.tokens.register("MODS_DIR", FMLPaths.MODSDIR.get()); - - final Path additionalPluginsDirectory = Path.of(this.tokens.replace(this.config.additionalPluginsDirectory())); - Files.createDirectories(additionalPluginsDirectory); - this.pluginDirectories = List.of(FMLPaths.MODSDIR.get(), additionalPluginsDirectory); - } - - @Override - public String version() { - return this.environment.getProperty(IEnvironment.Keys.VERSION.get()).orElse("dev"); - } - - @Override - public Logger logger() { - return this.logger; + public ForgePluginPlatform() { + this.discovery = new ForgePluginDiscovery(this.environment()); } @Override @@ -104,17 +59,34 @@ public Path configDirectory() { } @Override - public LaunchConfig config() { - return this.config; + public Path modsDirectory() { + return FMLPaths.MODSDIR.get(); + } + + @Override + public ForgePluginDiscovery discovery() { + return this.discovery; } @Override - public TokenReplacement tokens() { - return this.tokens; + public JVMPluginResource create(final Path[] paths) { + return new SecureJarPluginResource(paths); } @Override - public List pluginDirectories() { - return this.pluginDirectories; + public void addLoaderCloseCallback(final AutoCloseable closeable) { + ForgePluginPlatform.closeCallbacks.add(closeable); + } + + static { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + for (final AutoCloseable closeCallback : ForgePluginPlatform.closeCallbacks) { + try { + closeCallback.close(); + } catch (Exception e) { + PluginPlatform.LOGGER.error("Failed to run close callback {}", closeCallback, e); + } + } + }, "Sponge shutdown thread")); } } diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/ForgePluginDiscovery.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/ForgePluginDiscovery.java new file mode 100644 index 00000000000..d35873407ac --- /dev/null +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/ForgePluginDiscovery.java @@ -0,0 +1,77 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.applaunch.plugin.discovery; + +import cpw.mods.jarhandling.SecureJar; +import net.minecraftforge.securemodules.SecureModuleClassLoader; +import net.minecraftforge.securemodules.SecureModuleFinder; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.common.applaunch.plugin.discovery.SpongeJVMPluginResource; +import org.spongepowered.plugin.Environment; + +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +public class ForgePluginDiscovery extends PluginDiscovery { + private ModuleLayer currentLayer; + private ClassLoader currentLoader; + + public ForgePluginDiscovery(Environment environment) { + super(environment); + this.currentLayer = getClass().getModule().getLayer(); + this.currentLoader = getClass().getClassLoader(); + } + + @Override + protected ServiceLoader newServiceLoader(final Class serviceClass) { + return ServiceLoader.load(this.currentLayer, serviceClass); + } + + @Override + protected void appendDiscoveryServices(final List resources, final int batch) { + final ModuleLayer parentLayer = this.currentLayer; + final ClassLoader parentLoader = this.currentLoader; + + final List jars = new ArrayList<>(); + final List moduleNames = new ArrayList<>(); + + for (final SpongeJVMPluginResource resource : resources) { + final SecureJar jar = ((SecureJarPluginResource) resource).jar(); + jars.add(jar); + moduleNames.add(jar.name()); + } + + final Configuration config = parentLayer.configuration().resolveAndBind(SecureModuleFinder.of(jars), ModuleFinder.of(), moduleNames); + final ClassLoader loader = new SecureModuleClassLoader("SPONGE-DISCOVERY-BATCH-" + batch, null, config, List.of(parentLayer), List.of(parentLoader)); + + this.currentLayer = this.currentLayer.defineModulesWithOneLoader(config, loader); + this.currentLoader = loader; + + this.environment.logger().debug("Built new service layer {} on top of {}.", loader.getName(), parentLoader.getName()); + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/PluginJarMetadata.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/PluginJarMetadata.java similarity index 55% rename from vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/PluginJarMetadata.java rename to forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/PluginJarMetadata.java index 82b71be1464..d60eb3eff57 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/PluginJarMetadata.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/PluginJarMetadata.java @@ -22,11 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.applaunch.plugin.resource; +package org.spongepowered.forge.applaunch.plugin.discovery; import cpw.mods.jarhandling.JarMetadata; import cpw.mods.jarhandling.SecureJar; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.PluginMetadata; import java.lang.module.ModuleDescriptor; @@ -35,71 +34,47 @@ import java.util.Arrays; public class PluginJarMetadata implements JarMetadata { - private final SecureJar jar; - private final Path[] paths; - - private boolean initialized = false; - private ModuleDescriptor descriptor; + private final ModuleDescriptor descriptor; public PluginJarMetadata(final SecureJar jar, final Path[] paths) { - this.jar = jar; - this.paths = paths; - } - - public void init(final PluginMetadata plugin) { - if (this.initialized) { - throw new IllegalStateException("Metadata already initialized"); - } - this.initialized = true; - if (plugin == null) { - if (this.jar.moduleDataProvider().findFile("module-info.class").isEmpty() - && Arrays.stream(this.paths).allMatch(Files::isDirectory)) { - this.descriptor = PluginJarMetadata.createDescriptor(this.jar, this.jar.getPackages() - .stream() - .sorted() - .findFirst() - .orElseThrow(), null); - } else { - this.descriptor = JarMetadata.from(this.jar, this.paths).descriptor(); - } - } else { - this.descriptor = PluginJarMetadata.createDescriptor(this.jar, plugin.id().replace('-', '_'), plugin.version().toString()); - } + this.descriptor = PluginJarMetadata.standardDescriptor(jar, paths); } - private void checkInitialized() { - if (!this.initialized) { - throw new IllegalStateException("Metadata not initialized"); - } + public PluginJarMetadata(final SecureJar jar, final PluginMetadata plugin) { + this.descriptor = PluginJarMetadata.pluginDescriptor(jar, plugin); } @Override public String name() { - this.checkInitialized(); return this.descriptor.name(); } @Override public String version() { - this.checkInitialized(); return this.descriptor.rawVersion().orElse(null); } @Override public ModuleDescriptor descriptor() { - this.checkInitialized(); return this.descriptor; } - private static ModuleDescriptor createDescriptor(final SecureJar jar, final String name, final @Nullable String version) { - final ModuleDescriptor.Builder builder = ModuleDescriptor.newAutomaticModule(name) - .packages(jar.getPackages()); - if (version != null) { - builder.version(version); + private static ModuleDescriptor pluginDescriptor(final SecureJar jar, final PluginMetadata plugin) { + return PluginJarMetadata.descriptorBuilder(jar, plugin.id()).version(plugin.version().toString()).build(); + } + + private static ModuleDescriptor standardDescriptor(final SecureJar jar, final Path[] paths) { + if (jar.moduleDataProvider().findFile("module-info.class").isEmpty() && Arrays.stream(paths).allMatch(Files::isDirectory)) { + return PluginJarMetadata.descriptorBuilder(jar, jar.getPackages().stream().sorted().findFirst().orElseThrow()).build(); } + return JarMetadata.from(jar, paths).descriptor(); + } + + private static ModuleDescriptor.Builder descriptorBuilder(final SecureJar jar, final String name) { + final ModuleDescriptor.Builder builder = ModuleDescriptor.newAutomaticModule(name).packages(jar.getPackages()); jar.getProviders().stream() .filter(p -> !p.providers().isEmpty()) .forEach(p -> builder.provides(p.serviceName(), p.providers())); - return builder.build(); + return builder; } } diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/SecureJarPluginResource.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/SecureJarPluginResource.java new file mode 100644 index 00000000000..aaf0c383b83 --- /dev/null +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/SecureJarPluginResource.java @@ -0,0 +1,124 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.applaunch.plugin.discovery; + +import cpw.mods.jarhandling.SecureJar; +import net.minecraftforge.fml.loading.moddiscovery.ModJarMetadata; +import org.spongepowered.common.applaunch.plugin.discovery.SpongeJVMPluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; + +import java.io.InputStream; +import java.lang.module.ModuleDescriptor; +import java.net.URI; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.jar.Manifest; + +public final class SecureJarPluginResource implements SpongeJVMPluginResource { + private final Path[] pathsArray; + private final List paths; + private final SecureJar jar; + + public SecureJarPluginResource(final Path[] paths) { + Objects.requireNonNull(paths, "paths"); + if (paths.length == 0) { + throw new IllegalArgumentException("Need at least one path"); + } + this.pathsArray = paths; + this.paths = List.of(paths); + this.jar = SecureJar.from(jar -> new PluginJarMetadata(jar, paths), paths); + } + + /** + * We can't determine all paths from the jar. Use this constructor as a last resort. + */ + public SecureJarPluginResource(final SecureJar jar) { + Objects.requireNonNull(jar, "jar"); + this.pathsArray = new Path[]{jar.getPrimaryPath()}; + this.paths = List.of(jar.getPrimaryPath()); + this.jar = jar; + } + + public SecureJar jar() { + return this.jar; + } + + public SecureJar pluginJar(final PluginMetadata metadata) { + return SecureJar.from(jar -> new PluginJarMetadata(jar, metadata), this.pathsArray); + } + + @SuppressWarnings("UnstableApiUsage") + public SecureJar modJar(final ModJarMetadata metadata) { + return SecureJar.from(jar -> metadata, this.pathsArray); + } + + @Override + public List paths() { + return this.paths; + } + + @Override + public Optional locate(final String path) { + return this.jar.moduleDataProvider().findFile(Objects.requireNonNull(path, "path")); + } + + @Override + public Optional open(final String path) { + return this.jar.moduleDataProvider().open(Objects.requireNonNull(path, "path")); + } + + @Override + public Manifest manifest() { + return this.jar.moduleDataProvider().getManifest(); + } + + @Override + public ModuleDescriptor module() { + return this.jar.moduleDataProvider().descriptor(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof SecureJarPluginResource that)) { + return false; + } + return this.paths.equals(that.paths); + } + + @Override + public int hashCode() { + return this.paths.hashCode(); + } + + @Override + public String toString() { + return "SecureJarPluginResource[paths=" + this.paths + "]"; + } +} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/SpongeForgeResourceLocator.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/SpongeForgeResourceLocator.java new file mode 100644 index 00000000000..176e04541b9 --- /dev/null +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/discovery/SpongeForgeResourceLocator.java @@ -0,0 +1,71 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.applaunch.plugin.discovery; + +import com.google.common.collect.ImmutableMap; +import net.minecraftforge.fml.loading.FMLEnvironment; +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.discovery.UnknownResourceStrategy; + +import java.net.URI; +import java.net.URL; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.util.*; + +public final class SpongeForgeResourceLocator implements PluginResourceLocator { + + @Override + public String name() { + return "spongeforge"; + } + + @Override + public Collection locatePluginResources(final Environment environment) throws Exception { + if (!FMLEnvironment.production) { + return Collections.emptyList(); + } + + final URL rootJar = SpongeForgeResourceLocator.class.getProtectionDomain().getCodeSource().getLocation(); + final FileSystem fs = FileSystems.getFileSystem(rootJar.toURI()); // FML has already opened a file system for this jar + return Files.list(fs.getPath("jars")) + .filter(path -> path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".jar")) + .map(path -> { + try { + URI jij = new URI("jij:" + path.toAbsolutePath().toUri().getRawSchemeSpecificPart()).normalize(); + final Map env = ImmutableMap.of("packagePath", path); + FileSystem jijFS = FileSystems.newFileSystem(jij, env); + return jijFS.getPath("/"); // root of the archive to load + } catch (Exception e) { + throw new RuntimeException(e); + } + }) + .map((path) -> new Result(JVMPluginResource.create(environment, path), UnknownResourceStrategy.WARN)) + .toList(); + } +} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/metadata/PluginMetadataConverter.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/metadata/PluginMetadataConverter.java new file mode 100644 index 00000000000..4887354ecb0 --- /dev/null +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/plugin/metadata/PluginMetadataConverter.java @@ -0,0 +1,104 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.applaunch.plugin.metadata; + +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.forgespi.language.IModFileInfo; +import net.minecraftforge.forgespi.language.IModInfo; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.metadata.builtin.InheritableMetadata; +import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; +import org.spongepowered.plugin.metadata.model.PluginContributor; +import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginLinks; +import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; + +import java.net.URISyntaxException; +import java.net.URL; +import java.util.List; +import java.util.Optional; + +public final class PluginMetadataConverter { + private static final VersionRange ANY_VERSION; + + static { + try { + ANY_VERSION = VersionRange.createFromVersionSpec("*"); + } catch (InvalidVersionSpecificationException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("UnstableApiUsage") + public static PluginMetadata modToPlugin(final IModInfo info) { + final List loaders = info.getOwningFile().requiredLanguageLoaders(); + final PluginLoaderSpecification loaderSpec; + if (loaders.isEmpty()) { + loaderSpec = new PluginLoaderSpecification("forge", PluginMetadataConverter.ANY_VERSION); + } else { + final VersionRange loaderVersion = loaders.getFirst().acceptedVersions(); + loaderSpec = new PluginLoaderSpecification("forge:" + loaders.getFirst().languageName(), loaderVersion == null ? PluginMetadataConverter.ANY_VERSION : loaderVersion); + } + + final InheritableMetadata.Builder builder = InheritableMetadata.builder() + .name(info.getDisplayName()) + .version(info.getVersion()) + .loader(loaderSpec) + .description(info.getDescription()) + .properties(info.getModProperties()); + + if (info instanceof ModInfo modInfo) { + builder.addContributor(new PluginContributor(modInfo.getConfigElement("authors").orElse("unknown").toString(), Optional.empty())); + try { + final URL issueURL = modInfo.getOwningFile().getIssueURL(); + builder.links(new PluginLinks(null, null, issueURL == null ? null : issueURL.toURI())); + } catch (URISyntaxException ignored) {} + } + + for (final IModInfo.ModVersion dependency : info.getDependencies()) { + builder.addDependency(new PluginDependency( + dependency.getModId(), + dependency.getVersionRange(), + PluginMetadataConverter.orderingToLoad(dependency.getOrdering()), + !dependency.isMandatory() + )); + } + + return StandardPluginMetadata.builder() + .id(info.getModId()) + .override(builder.build()) + .build(); + } + + private static PluginDependency.LoadOrder orderingToLoad(final IModInfo.Ordering ordering) { + return switch (ordering) { + case BEFORE -> PluginDependency.LoadOrder.BEFORE; + case AFTER -> PluginDependency.LoadOrder.AFTER; + case NONE -> PluginDependency.LoadOrder.UNDEFINED; + }; + } +} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeAccessWidenerTransformer.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeAccessWidenerTransformer.java new file mode 100644 index 00000000000..3f378d96a9d --- /dev/null +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeAccessWidenerTransformer.java @@ -0,0 +1,88 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.applaunch.transformation; + +import cpw.mods.jarhandling.SecureJar; +import cpw.mods.modlauncher.api.ITransformer; +import cpw.mods.modlauncher.api.ITransformerActivity; +import cpw.mods.modlauncher.api.ITransformerVotingContext; +import cpw.mods.modlauncher.api.TransformerVoteResult; +import net.minecraftforge.fml.loading.LoadingModList; +import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.common.applaunch.transformation.AccessWidenerTransformer; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.stream.Collectors; + +public class ForgeAccessWidenerTransformer extends AccessWidenerTransformer implements ITransformer { + private static final String[] LABELS = { AccessWidenerTransformer.NAME }; + + @Override + public String[] labels() { + return LABELS; + } + + @Override + public ClassNode transform(final ClassNode input, final ITransformerVotingContext context) { + return this.transform(input); + } + + @Override + public TransformerVoteResult castVote(final ITransformerVotingContext context) { + return context.getReason().equals(ITransformerActivity.CLASSLOADING_REASON) ? TransformerVoteResult.YES : TransformerVoteResult.NO; + } + + @Override + public Set targets() { + return this.targetClasses().stream().map(Target::targetClass).collect(Collectors.toSet()); + } + + @Override + protected Collection collectResources() { + final Collection resources = new ArrayList<>(); + for (ModFileInfo fileInfo : LoadingModList.getModFiles()) { + final SecureJar jar = fileInfo.getFile().getSecureJar(); + final Attributes attributes = jar.moduleDataProvider().getManifest().getMainAttributes(); + + final String attribute = attributes.getValue(AccessWidenerTransformer.MANIFEST_ATTRIBUTE); + if (attribute != null) { + for (final String path : attribute.split(",")) { + try { + resources.add(jar.getPath(path).toUri().toURL()); + } catch (final MalformedURLException e) { + LOGGER.warn("Failed to locate access widener {} from {}", path, fileInfo.getFile().getFileName(), e); + } + } + } + } + return resources; + } +} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ListenerTransformer.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeListenerTransformer.java similarity index 86% rename from forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ListenerTransformer.java rename to forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeListenerTransformer.java index 4bbaab1e6f4..da945cd4d8f 100644 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ListenerTransformer.java +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeListenerTransformer.java @@ -31,36 +31,38 @@ import net.minecraftforge.fml.loading.LoadingModList; import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; import net.minecraftforge.forgespi.language.ModFileScanData; -import org.checkerframework.checker.nullness.qual.NonNull; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; -import org.spongepowered.transformers.modlauncher.ListenerTransformerHelper; +import org.spongepowered.common.applaunch.transformation.ListenerTransformer; import java.util.HashSet; import java.util.Set; -public class ListenerTransformer implements ITransformer { +public class ForgeListenerTransformer extends ListenerTransformer implements ITransformer { + private static final String[] LABELS = { ListenerTransformer.NAME }; + + @Override + public String[] labels() { + return LABELS; + } - @NonNull @Override public ClassNode transform(final ClassNode input, final ITransformerVotingContext context) { - ListenerTransformerHelper.transform(input); + this.transform(input); return input; } - @NonNull @Override public TransformerVoteResult castVote(final ITransformerVotingContext context) { return context.getReason().equals(ITransformerActivity.CLASSLOADING_REASON) ? TransformerVoteResult.YES : TransformerVoteResult.NO; } - @NonNull @Override public Set targets() { - final Type listenerType = Type.getType(ListenerTransformerHelper.LISTENER_DESC); + final Type listenerType = Type.getType(ListenerTransformer.ANNOTATION_DESC); final Set listenerClasses = new HashSet<>(); - for (ModFileInfo fileInfo : LoadingModList.get().getModFiles()) { + for (ModFileInfo fileInfo : LoadingModList.getModFiles()) { for (ModFileScanData.AnnotationData annotation : fileInfo.getFile().getScanResult().getAnnotations()) { if (listenerType.equals(annotation.annotationType())) { listenerClasses.add(annotation.clazz()); diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeSuperclassChangeTransformer.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeSuperclassChangeTransformer.java new file mode 100644 index 00000000000..f29fa4ce645 --- /dev/null +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeSuperclassChangeTransformer.java @@ -0,0 +1,93 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.applaunch.transformation; + +import cpw.mods.jarhandling.SecureJar; +import cpw.mods.modlauncher.api.ITransformer; +import cpw.mods.modlauncher.api.ITransformerActivity; +import cpw.mods.modlauncher.api.ITransformerVotingContext; +import cpw.mods.modlauncher.api.TransformerVoteResult; +import net.minecraftforge.fml.loading.LoadingModList; +import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.common.applaunch.transformation.SuperclassChangeTransformer; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.stream.Collectors; + +public class ForgeSuperclassChangeTransformer extends SuperclassChangeTransformer implements ITransformer { + private static final String MIXIN_PLUGIN_REASON = "mixin"; + private static final String[] LABELS = { SuperclassChangeTransformer.NAME }; + + @Override + public String[] labels() { + return LABELS; + } + + @Override + public ClassNode transform(final ClassNode input, final ITransformerVotingContext context) { + this.transform(input); + return input; + } + + @Override + public TransformerVoteResult castVote(final ITransformerVotingContext context) { + return switch (context.getReason()) { + case ITransformerActivity.CLASSLOADING_REASON, ForgeSuperclassChangeTransformer.MIXIN_PLUGIN_REASON -> TransformerVoteResult.YES; + default -> TransformerVoteResult.NO; + }; + } + + @Override + public Set targets() { + return this.targetClasses().stream().map(Target::targetClass).collect(Collectors.toSet()); + } + + @Override + protected Collection collectResources() { + final Collection resources = new ArrayList<>(); + for (ModFileInfo fileInfo : LoadingModList.getModFiles()) { + final SecureJar jar = fileInfo.getFile().getSecureJar(); + final Attributes attributes = jar.moduleDataProvider().getManifest().getMainAttributes(); + + final String attribute = attributes.getValue(SuperclassChangeTransformer.MANIFEST_ATTRIBUTE); + if (attribute != null) { + for (final String path : attribute.split(",")) { + try { + resources.add(jar.getPath(path).toUri().toURL()); + } catch (final MalformedURLException e) { + LOGGER.warn("Failed to locate superclass changer {} from {}", path, fileInfo.getFile().getFileName(), e); + } + } + } + } + return resources; + } +} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeTransformationService.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeTransformationService.java new file mode 100644 index 00000000000..3fbfb913fb3 --- /dev/null +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/ForgeTransformationService.java @@ -0,0 +1,51 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.applaunch.transformation; + +import cpw.mods.modlauncher.api.IEnvironment; +import cpw.mods.modlauncher.api.ITransformationService; +import cpw.mods.modlauncher.api.ITransformer; + +import java.util.List; +import java.util.Set; + +public class ForgeTransformationService implements ITransformationService { + @Override + public String name() { + return "spongeforge"; + } + + @Override + public void initialize(final IEnvironment environment) {} + + @Override + public void onLoad(final IEnvironment env, final Set otherServices) {} + + @SuppressWarnings("rawtypes") + @Override + public List transformers() { + return List.of(new ForgeAccessWidenerTransformer(), new ForgeListenerTransformer(), new ForgeSuperclassChangeTransformer()); + } +} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/JacocoPluginService.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/JacocoPluginService.java new file mode 100644 index 00000000000..40b919f9cba --- /dev/null +++ b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/JacocoPluginService.java @@ -0,0 +1,62 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.applaunch.transformation; + +import cpw.mods.modlauncher.TransformingClassLoader; +import cpw.mods.modlauncher.api.ITransformerActivity; +import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.common.applaunch.transformation.jacoco.JacocoTransformer; + +import java.util.EnumSet; + +public class JacocoPluginService extends JacocoTransformer implements ILaunchPluginService { + private static final EnumSet YAY = EnumSet.of(Phase.BEFORE); + private static final EnumSet NAY = EnumSet.noneOf(Phase.class); + + @Override + public String name() { + return JacocoTransformer.NAME; + } + + @Override + protected boolean isTransformingClassloader(ClassLoader loader) { + return loader instanceof TransformingClassLoader; + } + + @Override + public EnumSet handlesClass(Type classType, boolean isEmpty) { + return this.disabled || isEmpty ? NAY : YAY; + } + + @Override + public int processClassWithFlags(final Phase phase, final ClassNode classNode, final Type classType, final String reason) { + if (ITransformerActivity.COMPUTING_FRAMES_REASON.equals(reason)) { + return ComputeFlags.NO_REWRITE; + } + return this.transform(classNode) ? ComputeFlags.SIMPLE_REWRITE : ComputeFlags.NO_REWRITE; + } +} diff --git a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/SpongeForgeTransformationService.java b/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/SpongeForgeTransformationService.java deleted file mode 100644 index d8e27aad5b3..00000000000 --- a/forge/src/applaunch/java/org/spongepowered/forge/applaunch/transformation/SpongeForgeTransformationService.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.applaunch.transformation; - -import cpw.mods.jarhandling.SecureJar; -import cpw.mods.modlauncher.Environment; -import cpw.mods.modlauncher.Launcher; -import cpw.mods.modlauncher.api.IEnvironment; -import cpw.mods.modlauncher.api.IModuleLayerManager; -import cpw.mods.modlauncher.api.ITransformationService; -import cpw.mods.modlauncher.api.ITransformer; -import net.minecraftforge.fml.loading.LoadingModList; -import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.transformers.modlauncher.AccessWidenerTransformationService; -import org.spongepowered.transformers.modlauncher.SuperclassChanger; - -import java.net.MalformedURLException; -import java.util.List; -import java.util.Set; -import java.util.jar.Attributes; - -public class SpongeForgeTransformationService implements ITransformationService { - private static final Logger LOGGER = LogManager.getLogger(); - - @NonNull - @Override - public String name() { - return "spongeforge"; - } - - @Override - public void initialize(final IEnvironment environment) { - } - - @Override - public void onLoad(final IEnvironment env, final Set otherServices) { - } - - @Override - public List completeScan(IModuleLayerManager layerManager) { - final Environment env = Launcher.INSTANCE.environment(); - final AccessWidenerTransformationService accessWidener = env.getProperty(AccessWidenerTransformationService.INSTANCE.get()).orElse(null); - final SuperclassChanger superclassChanger = env.getProperty(SuperclassChanger.INSTANCE.get()).orElse(null); - - if (accessWidener != null || superclassChanger != null) { - // TODO move this inside modlauncher-transformers when updating SpongeVanilla to latest ModLauncher - for (ModFileInfo fileInfo : LoadingModList.get().getModFiles()) { - final SecureJar jar = fileInfo.getFile().getSecureJar(); - final Attributes attributes = jar.moduleDataProvider().getManifest().getMainAttributes(); - - if (accessWidener != null) { - final String awPaths = attributes.getValue("Access-Widener"); - if (awPaths != null) { - final String jarFileName = fileInfo.getFile().getFileName(); - LOGGER.debug("Registering access wideners from {} ...", jarFileName); - - for (final String awPath : awPaths.split(",")) { - try { - accessWidener.offerResource(jar.getPath(awPath).toUri().toURL(), awPath); - } catch (final MalformedURLException e) { - LOGGER.warn("Failed to register access widener {} from {}", awPath, jarFileName, e); - } - } - } - } - - if (superclassChanger != null) { - final String scPaths = attributes.getValue("Superclass-Transformer"); - if (scPaths != null) { - final String jarFileName = fileInfo.getFile().getFileName(); - LOGGER.debug("Registering superclass changers from {} ...", jarFileName); - - for (final String scPath : scPaths.split(",")) { - try { - superclassChanger.offerResource(jar.getPath(scPath).toUri().toURL(), scPath); - } catch (final MalformedURLException e) { - LOGGER.warn("Failed to register superclass changer {} from {}", scPath, jarFileName, e); - } - } - } - } - } - } - - return List.of(); - } - - @NonNull - @Override - @SuppressWarnings("rawtypes") // :) - public List transformers() { - return List.of(new ListenerTransformer()); - } -} diff --git a/forge/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService b/forge/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService index ced7680d190..a346646d5ea 100644 --- a/forge/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService +++ b/forge/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService @@ -1 +1 @@ -org.spongepowered.forge.applaunch.transformation.SpongeForgeTransformationService +org.spongepowered.forge.applaunch.transformation.ForgeTransformationService diff --git a/forge/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService b/forge/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService new file mode 100644 index 00000000000..26f9b4d60bf --- /dev/null +++ b/forge/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService @@ -0,0 +1 @@ +org.spongepowered.forge.applaunch.transformation.JacocoPluginService diff --git a/forge/src/applaunch/resources/META-INF/services/net.minecraftforge.forgespi.locating.IDependencyLocator b/forge/src/applaunch/resources/META-INF/services/net.minecraftforge.forgespi.locating.IDependencyLocator index 97ede6493ca..9e40bc73290 100644 --- a/forge/src/applaunch/resources/META-INF/services/net.minecraftforge.forgespi.locating.IDependencyLocator +++ b/forge/src/applaunch/resources/META-INF/services/net.minecraftforge.forgespi.locating.IDependencyLocator @@ -1 +1 @@ -org.spongepowered.forge.applaunch.loading.moddiscovery.SpongeForgeDependencyLocator \ No newline at end of file +org.spongepowered.forge.applaunch.mod.discovery.SpongeForgeDependencyLocator \ No newline at end of file diff --git a/forge/src/applaunch/resources/META-INF/services/net.minecraftforge.forgespi.locating.IModLocator b/forge/src/applaunch/resources/META-INF/services/net.minecraftforge.forgespi.locating.IModLocator index ad02e10d0d0..7274186d124 100644 --- a/forge/src/applaunch/resources/META-INF/services/net.minecraftforge.forgespi.locating.IModLocator +++ b/forge/src/applaunch/resources/META-INF/services/net.minecraftforge.forgespi.locating.IModLocator @@ -1,3 +1 @@ -org.spongepowered.forge.applaunch.loading.moddiscovery.SpongeForgeModLocator -org.spongepowered.forge.applaunch.loading.moddiscovery.locator.EnvironmentPluginLocator -org.spongepowered.forge.applaunch.loading.moddiscovery.locator.PluginsDirectoryLocator \ No newline at end of file +org.spongepowered.forge.applaunch.mod.discovery.SpongeForgeModLocator diff --git a/forge/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator b/forge/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator new file mode 100644 index 00000000000..c6bd66dcae5 --- /dev/null +++ b/forge/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator @@ -0,0 +1,2 @@ +org.spongepowered.forge.applaunch.plugin.discovery.SpongeForgeResourceLocator +org.spongepowered.common.applaunch.plugin.discovery.SpongePropertyResourceLocator diff --git a/forge/src/lang/java/org/spongepowered/forge/lang/provider/JavaPluginLanguageProvider.java b/forge/src/lang/java/org/spongepowered/forge/lang/provider/JavaPluginLanguageProvider.java deleted file mode 100644 index 5b5b7af6e68..00000000000 --- a/forge/src/lang/java/org/spongepowered/forge/lang/provider/JavaPluginLanguageProvider.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.lang.provider; - -import cpw.mods.modlauncher.api.LamdbaExceptionUtils; -import net.minecraftforge.fml.Logging; -import net.minecraftforge.fml.ModLoadingStage; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider; -import net.minecraftforge.forgespi.language.IModInfo; -import net.minecraftforge.forgespi.language.IModLanguageProvider; -import net.minecraftforge.forgespi.language.ModFileScanData; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.objectweb.asm.Type; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; - -public final class JavaPluginLanguageProvider extends FMLJavaModLanguageProvider { - - private static final Type PLUGIN_ANNOTATION = Type.getType("Lorg/spongepowered/plugin/builtin/jvm/Plugin;"); - - private final Logger logger; - - public JavaPluginLanguageProvider() { - this.logger = LogManager.getLogger(); - } - - @Override - public String name() { - return "java_plain"; - } - - @Override - public Consumer getFileVisitor() { - return scanResult -> { - final Map modTargetMap = scanResult.getAnnotations().stream() - .filter(ad -> ad.annotationType().equals(JavaPluginLanguageProvider.PLUGIN_ANNOTATION)) - .peek(ad -> this.logger.debug(Logging.SCAN, "Found @Plugin class {} with id {}", ad.clazz().getClassName(), ad.annotationData().get("value"))) - .map(ad -> new PluginTarget(ad.clazz().getClassName(), JavaPluginLanguageProvider.fixPluginId((String) ad.annotationData().get("value")))) - .collect(Collectors.toMap(PluginTarget::getPlugin, Function.identity(), (a,b)->a)); - scanResult.addLanguageLoader(modTargetMap); - }; - } - - private static String fixPluginId(final String id) { - return id.replace('-', '_'); - } - - private static final class PluginTarget implements IModLanguageProvider.IModLanguageLoader { - - private final Logger logger; - private final String className; - private final String plugin; - - private PluginTarget(final String className, final String plugin) { - this.logger = LogManager.getLogger(); - this.className = className; - this.plugin = plugin; - } - - @Override - public T loadMod(final IModInfo info, final ModFileScanData modFileScanData, final ModuleLayer gameLayer) { - // The following is adapted from FMLJavaModLanguageProvider.FMLModTarget - - // This language class is loaded in the system level classloader - before the game even starts - // So we must treat container construction as an arms length operation, and load the container - // in the classloader of the game - the context classloader is appropriate here. - try { - final Class pluginContainer = Class.forName( - "org.spongepowered.forge.launch.plugin.PluginModContainer", true, Thread.currentThread().getContextClassLoader()); - this.logger.debug(Logging.LOADING, "Loading PluginModContainer from classloader {} - got {}", Thread.currentThread().getContextClassLoader(), pluginContainer.getClassLoader()); - final Constructor constructor = pluginContainer.getConstructor(IModInfo.class, String.class, ModFileScanData.class, ModuleLayer.class); - return (T) constructor.newInstance(info, className, modFileScanData, gameLayer); - } - // ALL exception handling has to be done through the classloader, because we're loaded in the wrong context, so any classes we just blind load will be in the wrong - // class loading context. Funky but works. - catch (final InvocationTargetException e) { - this.logger.fatal(Logging.LOADING, "Failed to build plugin", e); - final Class mle = (Class) LamdbaExceptionUtils.uncheck(()->Class.forName("net.minecraftforge.fml.ModLoadingException", true, Thread.currentThread().getContextClassLoader())); - if (mle.isInstance(e.getTargetException())) { - throw mle.cast(e.getTargetException()); - } else { - final Class mls = (Class) LamdbaExceptionUtils.uncheck(()->Class.forName("net.minecraftforge.fml.ModLoadingStage", true, Thread.currentThread().getContextClassLoader())); - throw LamdbaExceptionUtils.uncheck(()->LamdbaExceptionUtils.uncheck(()->mle.getConstructor(IModInfo.class, mls, String.class, Throwable.class)).newInstance(info, Enum.valueOf(mls, "CONSTRUCT"), "fml.modloading.failedtoloadmodclass", e)); - } - } - catch (final NoSuchMethodException | ClassNotFoundException | InstantiationException | IllegalAccessException e) { - this.logger.fatal(Logging.LOADING,"Unable to load PluginModContainer, excuse me?", e); - final Class mle = (Class)LamdbaExceptionUtils.uncheck(()->Class.forName("net.minecraftforge.fml.ModLoadingException", true, Thread.currentThread().getContextClassLoader())); - final Class mls = (Class) LamdbaExceptionUtils.uncheck(()->Class.forName("net.minecraftforge.fml.ModLoadingStage", true, Thread.currentThread().getContextClassLoader())); - throw LamdbaExceptionUtils.uncheck(()->LamdbaExceptionUtils.uncheck(()->mle.getConstructor(IModInfo.class, mls, String.class, Throwable.class)).newInstance(info, Enum.valueOf(mls, "CONSTRUCT"), "fml.modloading.failedtoloadmodclass", e)); - } - } - - public String getPlugin() { - return this.plugin; - } - } -} diff --git a/forge/src/lang/java/org/spongepowered/forge/lang/provider/SpongeLanguageProvider.java b/forge/src/lang/java/org/spongepowered/forge/lang/provider/SpongeLanguageProvider.java new file mode 100644 index 00000000000..2280157526a --- /dev/null +++ b/forge/src/lang/java/org/spongepowered/forge/lang/provider/SpongeLanguageProvider.java @@ -0,0 +1,84 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.lang.provider; + +import net.minecraftforge.fml.Logging; +import net.minecraftforge.fml.ModLoadingException; +import net.minecraftforge.fml.ModLoadingStage; +import net.minecraftforge.forgespi.language.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.function.Consumer; +import java.util.function.Supplier; + +@SuppressWarnings("unchecked") +public final class SpongeLanguageProvider implements IModLanguageProvider { + + @Override + public String name() { + return "sponge"; + } + + @Override + public Consumer getFileVisitor() { + return scanData -> { + final Map targets = (Map) scanData.getTargets(); + for (final IModFileInfo fileInfo : scanData.getIModInfoData()) { + for (final IModInfo modInfo : fileInfo.getMods()) { + targets.put(modInfo.getModId(), SpongeLanguageLoader.INSTANCE); + } + } + }; + } + + @Override + public > void consumeLifecycleEvent(final Supplier consumeEvent) {} + + private static final class SpongeLanguageLoader implements IModLanguageProvider.IModLanguageLoader { + private static final Logger LOGGER = LogManager.getLogger(); + private static final SpongeLanguageLoader INSTANCE = new SpongeLanguageLoader(); + + @Override + public T loadMod(final IModInfo info, final ModFileScanData scanData, final ModuleLayer layer) { + try { + final Module module = layer.findModule("spongeforge").orElseThrow(() -> new NoSuchElementException("Module spongeforge")); + final Class containerClass = Class.forName(module, "org.spongepowered.forge.launch.plugin.PluginModContainer"); + final Constructor constructor = containerClass.getConstructor(IModInfo.class, ModFileScanData.class, ModuleLayer.class); + return (T) constructor.newInstance(info, scanData, layer); + } catch (final InvocationTargetException e) { + SpongeLanguageLoader.LOGGER.fatal(Logging.LOADING, "Failed to build plugin", e); + throw new ModLoadingException(info, ModLoadingStage.CONSTRUCT, "Failed to build plugin", e); + } catch (final Exception e) { + SpongeLanguageLoader.LOGGER.fatal(Logging.LOADING, "Unable to load PluginModContainer", e); + throw new ModLoadingException(info, ModLoadingStage.CONSTRUCT, "Unable to load PluginModContainer", e); + } + } + } +} diff --git a/forge/src/lang/resources/META-INF/services/net.minecraftforge.forgespi.language.IModLanguageProvider b/forge/src/lang/resources/META-INF/services/net.minecraftforge.forgespi.language.IModLanguageProvider index 1f6b5fa2dfd..fd8849eb51b 100644 --- a/forge/src/lang/resources/META-INF/services/net.minecraftforge.forgespi.language.IModLanguageProvider +++ b/forge/src/lang/resources/META-INF/services/net.minecraftforge.forgespi.language.IModLanguageProvider @@ -1 +1 @@ -org.spongepowered.forge.lang.provider.JavaPluginLanguageProvider \ No newline at end of file +org.spongepowered.forge.lang.provider.SpongeLanguageProvider \ No newline at end of file diff --git a/forge/src/main/java/org/spongepowered/forge/SpongeAPIMod.java b/forge/src/main/java/org/spongepowered/forge/SpongeAPIMod.java deleted file mode 100644 index 863a0654753..00000000000 --- a/forge/src/main/java/org/spongepowered/forge/SpongeAPIMod.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge; - -import net.minecraftforge.fml.common.Mod; - -@Mod("spongeapi") -public class SpongeAPIMod { -} diff --git a/forge/src/main/java/org/spongepowered/forge/SpongeForgeMod.java b/forge/src/main/java/org/spongepowered/forge/SpongeForge.java similarity index 78% rename from forge/src/main/java/org/spongepowered/forge/SpongeForgeMod.java rename to forge/src/main/java/org/spongepowered/forge/SpongeForge.java index e51fc5b00ca..1991fe3f05d 100644 --- a/forge/src/main/java/org/spongepowered/forge/SpongeForgeMod.java +++ b/forge/src/main/java/org/spongepowered/forge/SpongeForge.java @@ -24,51 +24,47 @@ */ package org.spongepowered.forge; +import com.google.inject.Inject; import net.minecraft.client.Minecraft; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.EntityAttributeCreationEvent; import net.minecraftforge.event.server.ServerAboutToStartEvent; import net.minecraftforge.event.server.ServerStartedEvent; import net.minecraftforge.event.server.ServerStoppingEvent; -import net.minecraftforge.eventbus.api.IEventBus; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.eventbus.api.bus.BusGroup; +import net.minecraftforge.eventbus.api.listener.SubscribeEvent; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.spongepowered.api.Client; import org.spongepowered.api.Server; -import org.spongepowered.api.Sponge; import org.spongepowered.common.entity.SpongeEntityTypes; import org.spongepowered.common.entity.living.human.HumanEntity; import org.spongepowered.common.hooks.PlatformHooks; import org.spongepowered.common.launch.Launch; import org.spongepowered.common.launch.Lifecycle; import org.spongepowered.common.launch.config.core.ConfigHandle; -import org.spongepowered.common.network.channel.SpongeChannelManager; -import org.spongepowered.common.network.packet.SpongePacketHandler; import org.spongepowered.forge.hook.ForgeChannelHooks; import org.spongepowered.forge.hook.ForgeEventHooks; import org.spongepowered.forge.hook.ForgeGeneralHooks; import org.spongepowered.forge.hook.ForgeItemHooks; import org.spongepowered.forge.hook.ForgeWorldHooks; +import org.spongepowered.plugin.builtin.jvm.Plugin; -@Mod("spongeforge") -public final class SpongeForgeMod { +@Plugin("spongeforge") +public final class SpongeForge { private final Logger logger = LogManager.getLogger("spongeforge"); - public SpongeForgeMod() { + @Inject + public SpongeForge(final BusGroup group) { // WorldPersistenceHooks.addHook(SpongeLevelDataPersistence.INSTANCE); // TODO SF 1.19.4 - final IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus(); - // modBus: add all FML events with it - modBus.addListener(this::onCommonSetup); - modBus.addListener(this::onClientSetup); - modBus.addListener(this::onEntityAttributeCreationEvent); + FMLCommonSetupEvent.getBus(group).addListener(this::onCommonSetup); + FMLClientSetupEvent.getBus(group).addListener(this::onClientSetup); + EntityAttributeCreationEvent.BUS.addListener(this::onEntityAttributeCreationEvent); // annotation events, for non-FML things MinecraftForge.EVENT_BUS.register(this); @@ -82,24 +78,12 @@ public SpongeForgeMod() { } private void onCommonSetup(final FMLCommonSetupEvent event) { - final Lifecycle lifecycle = Launch.instance().lifecycle(); - lifecycle.callConstructEvent(); - lifecycle.callRegisterFactoryEvent(); - lifecycle.callRegisterBuilderEvent(); - lifecycle.callRegisterChannelEvent(); - lifecycle.establishGameServices(); - lifecycle.establishDataKeyListeners(); - - SpongePacketHandler.init((SpongeChannelManager) Sponge.channelManager()); - this.logger.info("SpongeForge v{} initialized", Launch.instance().platformPlugin().metadata().version()); } private void onClientSetup(final FMLClientSetupEvent event) { final Client minecraft = (Client) Minecraft.getInstance(); final Lifecycle lifecycle = Launch.instance().lifecycle(); - lifecycle.establishDataProviders(); - lifecycle.callRegisterDataEvent(); lifecycle.establishClientRegistries(minecraft); lifecycle.callStartingEngineEvent(minecraft); } @@ -110,11 +94,8 @@ public void onServerAboutToStart(final ServerAboutToStartEvent event) { ConfigHandle.setSaveSuppressed(false); final Lifecycle lifecycle = Launch.instance().lifecycle(); - lifecycle.establishServerServices(); - lifecycle.establishServerFeatures(); - lifecycle.establishServerRegistries((Server) event.getServer()); lifecycle.callStartingEngineEvent((Server) event.getServer()); } diff --git a/forge/src/main/java/org/spongepowered/forge/hook/ForgeChannelHooks.java b/forge/src/main/java/org/spongepowered/forge/hook/ForgeChannelHooks.java index e2556d0d697..399375f877d 100644 --- a/forge/src/main/java/org/spongepowered/forge/hook/ForgeChannelHooks.java +++ b/forge/src/main/java/org/spongepowered/forge/hook/ForgeChannelHooks.java @@ -26,7 +26,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraftforge.network.ForgePayload; import org.spongepowered.api.ResourceKey; import org.spongepowered.common.hooks.ChannelHooks; @@ -46,6 +46,6 @@ public void registerPlatformChannels(final Consumer channels) { final FriendlyByteBuf payload = (FriendlyByteBuf) RegisterChannelUtil.encodePayload(channels); - return new ForgePayload((ResourceLocation) (Object) Constants.Channels.REGISTER_KEY, null, b -> b.writeBytes(payload)); + return new ForgePayload((Identifier) (Object) Constants.Channels.REGISTER_KEY, null, b -> b.writeBytes(payload)); } } diff --git a/forge/src/main/java/org/spongepowered/forge/hook/ForgeItemHooks.java b/forge/src/main/java/org/spongepowered/forge/hook/ForgeItemHooks.java index 296d642e82a..03c25141938 100644 --- a/forge/src/main/java/org/spongepowered/forge/hook/ForgeItemHooks.java +++ b/forge/src/main/java/org/spongepowered/forge/hook/ForgeItemHooks.java @@ -24,6 +24,7 @@ */ package org.spongepowered.forge.hook; +import net.minecraft.core.Holder; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.enchantment.Enchantment; import org.spongepowered.common.hooks.ItemHooks; @@ -32,6 +33,6 @@ public class ForgeItemHooks implements ItemHooks { @Override public boolean canEnchantmentBeAppliedToItem(Enchantment enchantment, ItemStack stack) { - return stack.canApplyAtEnchantingTable(enchantment); + return stack.canApplyAtEnchantingTable(Holder.direct(enchantment)); } } diff --git a/forge/src/main/java/org/spongepowered/forge/hook/ForgeWorldHooks.java b/forge/src/main/java/org/spongepowered/forge/hook/ForgeWorldHooks.java index d5475bcd424..0e2ab6f4bf0 100644 --- a/forge/src/main/java/org/spongepowered/forge/hook/ForgeWorldHooks.java +++ b/forge/src/main/java/org/spongepowered/forge/hook/ForgeWorldHooks.java @@ -24,14 +24,18 @@ */ package org.spongepowered.forge.hook; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; -import net.minecraftforge.common.MinecraftForge; +import net.minecraft.world.level.dimension.LevelStem; import net.minecraftforge.event.level.LevelEvent; +import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.hooks.WorldHooks; public class ForgeWorldHooks implements WorldHooks { @@ -59,13 +63,18 @@ public boolean isRestoringBlocks(final Level world) { @Override public void postLoadWorld(final ServerLevel world) { - MinecraftForge.EVENT_BUS.post(new LevelEvent.Load(world)); + LevelEvent.Load.BUS.post(new LevelEvent.Load(world)); world.getServer().markWorldsDirty(); } @Override public void preUnloadWorld(final ServerLevel world) { - MinecraftForge.EVENT_BUS.post(new LevelEvent.Unload(world)); + LevelEvent.Unload.BUS.post(new LevelEvent.Unload(world)); world.getServer().markWorldsDirty(); } + + @Override + public Registry earlyRegistryAccess(ResourceKey> levelStem) { + return SpongeCommon.server().registryAccess().lookupOrThrow(Registries.LEVEL_STEM); + } } diff --git a/forge/src/main/java/org/spongepowered/forge/launch/ForgeLaunch.java b/forge/src/main/java/org/spongepowered/forge/launch/ForgeLaunch.java index 224441a417c..4f25d8a5b76 100644 --- a/forge/src/main/java/org/spongepowered/forge/launch/ForgeLaunch.java +++ b/forge/src/main/java/org/spongepowered/forge/launch/ForgeLaunch.java @@ -30,16 +30,19 @@ import com.google.inject.Module; import com.google.inject.Stage; import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.loading.FMLLoader; import org.spongepowered.common.applaunch.plugin.PluginPlatform; import org.spongepowered.common.inject.SpongeCommonModule; import org.spongepowered.common.inject.SpongeModule; import org.spongepowered.common.launch.Launch; +import org.spongepowered.forge.launch.inject.ModContainerModule; import org.spongepowered.forge.launch.inject.SpongeForgeModule; import org.spongepowered.forge.launch.plugin.ForgePluginManager; import org.spongepowered.plugin.PluginContainer; import java.util.List; +import java.util.Optional; public final class ForgeLaunch extends Launch { @@ -88,4 +91,9 @@ public PluginContainer platformPlugin() { return this.spongeForgePlugin; } + + @Override + public Optional modModule(final PluginContainer plugin) { + return ModList.getModContainerById(plugin.metadata().id()).map(ModContainerModule::new); + } } diff --git a/forge/src/main/java/org/spongepowered/forge/launch/bridge/event/SpongeEventBridge_Forge.java b/forge/src/main/java/org/spongepowered/forge/launch/bridge/event/SpongeEventBridge_Forge.java index 883959d5d1e..9a5dfc120ac 100644 --- a/forge/src/main/java/org/spongepowered/forge/launch/bridge/event/SpongeEventBridge_Forge.java +++ b/forge/src/main/java/org/spongepowered/forge/launch/bridge/event/SpongeEventBridge_Forge.java @@ -24,9 +24,7 @@ */ package org.spongepowered.forge.launch.bridge.event; -import net.minecraftforge.eventbus.api.Event; -import net.minecraftforge.eventbus.api.IEventBusInvokeDispatcher; -import net.minecraftforge.eventbus.api.IEventListener; +import net.minecraftforge.eventbus.internal.Event; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collection; @@ -46,14 +44,4 @@ public interface SpongeEventBridge_Forge { return null; } - /** - * If there is a specialist dispatcher that we need to use for - * this given event, it can be defined here. - * - * @return The dispatcher to use. - */ - default IEventBusInvokeDispatcher bridge$eventDispatcher() { - return IEventListener::invoke; - } - } diff --git a/forge/src/main/java/org/spongepowered/forge/launch/command/ForgeCommandManager.java b/forge/src/main/java/org/spongepowered/forge/launch/command/ForgeCommandManager.java index 973827201be..09261d141db 100644 --- a/forge/src/main/java/org/spongepowered/forge/launch/command/ForgeCommandManager.java +++ b/forge/src/main/java/org/spongepowered/forge/launch/command/ForgeCommandManager.java @@ -33,7 +33,6 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.kyori.adventure.text.Component; import net.minecraft.commands.CommandSourceStack; -import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.CommandEvent; import org.spongepowered.api.Game; import org.spongepowered.api.command.CommandCause; @@ -82,7 +81,7 @@ public CommandResult processCommand(final CommandCause cause, final CommandMappi // Relocated from Commands (injection short circuits it there) final CommandEvent event = new CommandEvent(parseResults); - if (MinecraftForge.EVENT_BUS.post(event)) { + if (CommandEvent.BUS.post(event)) { if (event.getException() != null) { Throwables.throwIfUnchecked(event.getException()); } diff --git a/forge/src/main/java/org/spongepowered/forge/launch/event/ForgeEventManager.java b/forge/src/main/java/org/spongepowered/forge/launch/event/ForgeEventManager.java index 72179293a4f..77a711a64a2 100644 --- a/forge/src/main/java/org/spongepowered/forge/launch/event/ForgeEventManager.java +++ b/forge/src/main/java/org/spongepowered/forge/launch/event/ForgeEventManager.java @@ -25,134 +25,31 @@ package org.spongepowered.forge.launch.event; import com.google.inject.Singleton; -import net.minecraftforge.eventbus.api.Event; -import net.minecraftforge.eventbus.api.EventPriority; -import net.minecraftforge.eventbus.api.GenericEvent; -import net.minecraftforge.eventbus.api.IEventBus; -import net.minecraftforge.eventbus.api.IEventBusInvokeDispatcher; -import net.minecraftforge.eventbus.api.IEventListener; -import org.checkerframework.checker.nullness.qual.Nullable; +import net.minecraftforge.common.EventBusMigrationHelper; +import net.minecraftforge.eventbus.internal.Event; import org.spongepowered.common.event.manager.RegisteredListener; import org.spongepowered.common.event.manager.SpongeEventManager; import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; -import org.spongepowered.forge.launch.bridge.event.SpongeEventBridge_Forge; import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.function.Consumer; @Singleton -public final class ForgeEventManager extends SpongeEventManager implements IEventBus { - - private final IEventBus wrappedEventBus; - - public ForgeEventManager(final IEventBus eventBus) { - this.wrappedEventBus = eventBus; - } - - // IEventBus - - @Override - public void register(final Object target) { - this.wrappedEventBus.register(target); - } - - @Override - public void addListener(final Consumer consumer) { - this.wrappedEventBus.addListener(consumer); - } - - @Override - public void addListener(final EventPriority priority, final Consumer consumer) { - this.wrappedEventBus.addListener(priority, consumer); - } - - @Override - public void addListener(final EventPriority priority, final boolean receiveCancelled, final Consumer consumer) { - this.wrappedEventBus.addListener(priority, receiveCancelled, consumer); - } - - @Override - public void addListener(final EventPriority priority, final boolean receiveCancelled, final Class eventType, final Consumer consumer) { - this.wrappedEventBus.addListener(priority, receiveCancelled, eventType, consumer); - } - - @Override - public , F> void addGenericListener(final Class genericClassFilter, final Consumer consumer) { - this.wrappedEventBus.addGenericListener(genericClassFilter, consumer); - } - - @Override - public , F> void addGenericListener(final Class genericClassFilter, final EventPriority priority, - final Consumer consumer) { - this.wrappedEventBus.addGenericListener(genericClassFilter, priority, consumer); - } - - @Override - public , F> void addGenericListener(final Class genericClassFilter, final EventPriority priority, - final boolean receiveCancelled, final Consumer consumer) { - this.wrappedEventBus.addGenericListener(genericClassFilter, priority, receiveCancelled, consumer); - } - - @Override - public , F> void addGenericListener(final Class genericClassFilter, final EventPriority priority, - final boolean receiveCancelled, final Class eventType, final Consumer consumer) { - this.wrappedEventBus.addGenericListener(genericClassFilter, priority, receiveCancelled, eventType, consumer); - } - - @Override - public void unregister(final Object object) { - this.wrappedEventBus.unregister(object); - } - - @Override - public boolean post(final Event event) { - return this.post(event, IEventListener::invoke); - } - - @Override - public boolean post(final Event event, final IEventBusInvokeDispatcher wrapper) { - if (event instanceof ForgeEventBridge_Forge forgeEvent) { - // intercept! - final org.spongepowered.api.event.@Nullable Event spongeEvent = forgeEvent.bridge$createSpongeEvent(); - if (spongeEvent != null) { - return this.postDualBus(spongeEvent, Collections.singleton(event), wrapper); - } - } - // Do as Forge does - SpongeVanilla has no role to play here. - return this.wrappedEventBus.post(event, wrapper); - } - - @Override - public void shutdown() { - this.wrappedEventBus.shutdown(); - } - - @Override - public void start() { - this.wrappedEventBus.start(); - } - - // EventManager +public final class ForgeEventManager extends SpongeEventManager { + private final EventBusMigrationHelper INSTANCE = EventBusMigrationHelper.INSTANCE; @Override public boolean post(final org.spongepowered.api.event.Event event) { - final SpongeEventBridge_Forge eventBridge = ((SpongeEventBridge_Forge) event); - final @Nullable Collection forgeEvents = eventBridge.bridge$createForgeEvents(); - if (forgeEvents == null || forgeEvents.isEmpty()) { - // Do as SpongeVanilla does - Forge has no role to play here. - return super.post(event); - } - return this.postDualBus(event, forgeEvents, eventBridge.bridge$eventDispatcher()); +// final SpongeEventBridge_Forge eventBridge = ((SpongeEventBridge_Forge) event); +// final @Nullable Collection forgeEvents = eventBridge.bridge$createForgeEvents(); + return super.post(event); } // Implementation @SuppressWarnings({"unchecked", "rawtypes"}) private boolean postDualBus( - final org.spongepowered.api.event.Event spongeEvent, final Collection forgeEvents, - final IEventBusInvokeDispatcher dispatcher) { + final org.spongepowered.api.event.Event spongeEvent, final Collection forgeEvents) { try (final NoExceptionClosable ignored = this.preparePost(spongeEvent)) { final RegisteredListener.Cache listeners = this.getHandlerCache(spongeEvent); final List> beforeModifications = listeners.beforeModifications(); @@ -167,7 +64,8 @@ private boolean postDualBus( } // Then, we fire all our Forge events for (final Event forgeEvent : forgeEvents) { - this.wrappedEventBus.post(forgeEvent, dispatcher); + +// INSTANCE.post(forgeEvent, dispatcher); // We must sync back the event's changes, if there are any. // For complex events, this will be a partial sync. ((ForgeEventBridge_Forge) forgeEvent).bridge$syncTo(spongeEvent); diff --git a/forge/src/main/java/org/spongepowered/forge/launch/event/SpongeEventBus.java b/forge/src/main/java/org/spongepowered/forge/launch/event/SpongeEventBus.java deleted file mode 100644 index 153ce5075e3..00000000000 --- a/forge/src/main/java/org/spongepowered/forge/launch/event/SpongeEventBus.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.launch.event; - -import net.minecraftforge.eventbus.BusBuilderImpl; -import net.minecraftforge.eventbus.EventBus; -import net.minecraftforge.eventbus.api.Event; -import net.minecraftforge.eventbus.api.EventPriority; -import net.minecraftforge.eventbus.api.IEventBusInvokeDispatcher; -import net.minecraftforge.eventbus.api.IEventExceptionHandler; -import net.minecraftforge.eventbus.api.IEventListener; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.event.CauseStackManager; -import org.spongepowered.common.event.manager.SpongeEventManager; -import org.spongepowered.common.event.tracking.PhaseContext; -import org.spongepowered.common.event.tracking.PhaseTracker; - -import java.lang.reflect.Field; -import java.util.Objects; - -public final class SpongeEventBus extends EventBus { - - // I hope ya'll like reflection... - private static Field busIDField; - private static Field exceptionHandlerField; - private static Field trackPhasesField; - private static Field shutdownField; - private static Field baseTypeField; - private static Field checkTypesOnDispatchField; - - static { - try { - SpongeEventBus.busIDField = EventBus.class.getDeclaredField("busID"); - SpongeEventBus.exceptionHandlerField = EventBus.class.getDeclaredField("exceptionHandler"); - SpongeEventBus.trackPhasesField = EventBus.class.getDeclaredField("trackPhases"); - SpongeEventBus.shutdownField = EventBus.class.getDeclaredField("shutdown"); - SpongeEventBus.baseTypeField = EventBus.class.getDeclaredField("baseType"); - SpongeEventBus.checkTypesOnDispatchField = EventBus.class.getDeclaredField("checkTypesOnDispatch"); - - } catch (final Exception ex) { - // Burn this to the ground - throw new RuntimeException(ex); - } - } - - // reflected fields that are stored again to prevent multiple reflection calls - private final int rbusID; - private final IEventExceptionHandler rexceptionHandler; - private final boolean rtrackPhases; - private boolean rshutdown; - private final Class rbaseType; - private final boolean rcheckTypesOnDispatch; - - public SpongeEventBus(final BusBuilderImpl busBuilder) { - super(busBuilder); - - // Sponge Start - I hope ya'll still like reflection - try { - SpongeEventBus.busIDField.setAccessible(true); - SpongeEventBus.exceptionHandlerField.setAccessible(true); - SpongeEventBus.trackPhasesField.setAccessible(true); - SpongeEventBus.shutdownField.setAccessible(true); - SpongeEventBus.baseTypeField.setAccessible(true); - SpongeEventBus.checkTypesOnDispatchField.setAccessible(true); - - this.rbusID = SpongeEventBus.busIDField.getInt(this); - this.rexceptionHandler = (IEventExceptionHandler) SpongeEventBus.exceptionHandlerField.get(this); - this.rtrackPhases = SpongeEventBus.trackPhasesField.getBoolean(this); - this.rshutdown = SpongeEventBus.shutdownField.getBoolean(this); - this.rbaseType = (Class) SpongeEventBus.baseTypeField.get(this); - this.rcheckTypesOnDispatch = SpongeEventBus.checkTypesOnDispatchField.getBoolean(this); - - SpongeEventBus.busIDField.setAccessible(false); - SpongeEventBus.exceptionHandlerField.setAccessible(false); - SpongeEventBus.trackPhasesField.setAccessible(false); - SpongeEventBus.shutdownField.setAccessible(false); - SpongeEventBus.baseTypeField.setAccessible(false); - SpongeEventBus.checkTypesOnDispatchField.setAccessible(false); - } catch (final Exception ex) { - // Burn this to the ground again - throw new RuntimeException(ex); - } - // Sponge End - } - - @Override - public boolean post(final Event event, final IEventBusInvokeDispatcher wrapper) { - // @formatter:off - - // Sponge - use the builder/member fields. Avoids reflection but remains very hacky... - if (this.rshutdown) return false; - if (this.rcheckTypesOnDispatch && !rbaseType.isInstance(event)) - { - throw new IllegalArgumentException("Cannot post event of type " + event.getClass().getSimpleName() + " to this event. Must match type: " + this.rbaseType.getSimpleName()); - } - - IEventListener[] listeners = event.getListenerList().getListeners(this.rbusID); - int index = 0; - for (; index < listeners.length; index++) - { - final IEventListener listener = listeners[index]; - if (!this.rtrackPhases && Objects.equals(listener.getClass(), EventPriority.class)) continue; - - try ( - final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame(); - final PhaseContext<@NonNull ?> context = SpongeEventManager.createListenerContext(event, null)) - { - if (context != null) { - context.buildAndSwitch(); - } - - wrapper.invoke(listener, event); - } catch (final Throwable t) { - this.rexceptionHandler.handleException(this, event, listeners, index, t); - } - } - return event.isCancelable() && event.isCanceled(); - - // @formatter:on - } - - @Override - public void shutdown() { - super.shutdown(); - this.rshutdown = true; - } - - @Override - public void start() { - super.start(); - this.rshutdown = false; - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/enchantment/EnchantmentTypeMixin_ItemStackLike.java b/forge/src/main/java/org/spongepowered/forge/launch/inject/ModContainerModule.java similarity index 67% rename from src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/enchantment/EnchantmentTypeMixin_ItemStackLike.java rename to forge/src/main/java/org/spongepowered/forge/launch/inject/ModContainerModule.java index 64fbebeb6ca..720e4cef49b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/enchantment/EnchantmentTypeMixin_ItemStackLike.java +++ b/forge/src/main/java/org/spongepowered/forge/launch/inject/ModContainerModule.java @@ -22,20 +22,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.itemstacklike.item.enchantment; +package org.spongepowered.forge.launch.inject; -import org.spongepowered.api.item.enchantment.EnchantmentType; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.asm.mixin.Mixin; +import com.google.inject.PrivateModule; +import net.minecraftforge.eventbus.api.bus.BusGroup; +import net.minecraftforge.fml.ModContainer; -@Mixin(value = EnchantmentType.class, remap = false) -public interface EnchantmentTypeMixin_ItemStackLike { +public final class ModContainerModule extends PrivateModule { + private final ModContainer modContainer; - default boolean canBeAppliedToStack(ItemStack stack) { - return ((EnchantmentType) this).canBeAppliedToStack(stack); + public ModContainerModule(final ModContainer modContainer) { + this.modContainer = modContainer; } - default boolean canBeAppliedByTable(ItemStack stack) { - return ((EnchantmentType) this).canBeAppliedByTable(stack); + @Override + protected void configure() { + this.bind(ModContainer.class).toInstance(this.modContainer); + this.bind(BusGroup.class).toInstance(this.modContainer.getModBusGroup()); } } diff --git a/forge/src/main/java/org/spongepowered/forge/launch/inject/SpongeForgeModule.java b/forge/src/main/java/org/spongepowered/forge/launch/inject/SpongeForgeModule.java index b9187264f17..ff83a86d3c7 100644 --- a/forge/src/main/java/org/spongepowered/forge/launch/inject/SpongeForgeModule.java +++ b/forge/src/main/java/org/spongepowered/forge/launch/inject/SpongeForgeModule.java @@ -25,7 +25,6 @@ package org.spongepowered.forge.launch.inject; import com.google.inject.AbstractModule; -import net.minecraftforge.common.MinecraftForge; import org.spongepowered.api.Platform; import org.spongepowered.api.event.EventManager; import org.spongepowered.common.command.manager.SpongeCommandManager; @@ -38,7 +37,7 @@ public final class SpongeForgeModule extends AbstractModule { @Override protected void configure() { this.bind(Platform.class).to(ForgePlatform.class); - this.bind(EventManager.class).toProvider(() -> (ForgeEventManager) MinecraftForge.EVENT_BUS); + this.bind(EventManager.class).toProvider(ForgeEventManager::new); this.bind(SpongeCommandManager.class).to(ForgeCommandManager.class); } diff --git a/forge/src/main/java/org/spongepowered/forge/launch/plugin/ForgePluginContainer.java b/forge/src/main/java/org/spongepowered/forge/launch/plugin/ForgePluginContainer.java index d4979335e54..5d0b3225708 100644 --- a/forge/src/main/java/org/spongepowered/forge/launch/plugin/ForgePluginContainer.java +++ b/forge/src/main/java/org/spongepowered/forge/launch/plugin/ForgePluginContainer.java @@ -25,36 +25,44 @@ package org.spongepowered.forge.launch.plugin; import com.google.common.collect.MapMaker; -import com.google.inject.Injector; import net.minecraftforge.fml.ModContainer; -import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.forgespi.locating.IModFile; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.spongepowered.common.launch.plugin.SpongePluginContainer; +import org.spongepowered.forge.applaunch.plugin.discovery.SecureJarPluginResource; +import org.spongepowered.forge.applaunch.plugin.metadata.PluginMetadataConverter; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.discovery.PluginResource; import org.spongepowered.plugin.metadata.PluginMetadata; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.Map; -import java.util.Objects; import java.util.Optional; -public class ForgePluginContainer implements SpongePluginContainer { +public class ForgePluginContainer implements PluginContainer { private final ModContainer modContainer; + private final IModFile modFile; - private Injector injector; private Logger logger; private PluginMetadata pluginMetadata; + private PluginResource pluginResource; private ForgePluginContainer(final ModContainer modContainer) { this.modContainer = modContainer; + this.modFile = modContainer.getModInfo().getOwningFile().getFile(); + } + + @Override + public PluginResource resource() { + if (this.pluginResource == null) { + this.pluginResource = new SecureJarPluginResource(this.modFile.getSecureJar()); + } + return this.pluginResource; } @Override public PluginMetadata metadata() { if (this.pluginMetadata == null) { - this.pluginMetadata = PluginMetadataConverter.modToPlugin((ModInfo) this.modContainer.getModInfo()); + this.pluginMetadata = PluginMetadataConverter.modToPlugin(this.modContainer.getModInfo()); } return this.pluginMetadata; } @@ -67,29 +75,18 @@ public Logger logger() { return this.logger; } - @Override - public Optional locateResource(String relative) { - final Path p = this.modContainer.getModInfo().getOwningFile().getFile().findResource(Objects.requireNonNull(relative, "relative")); - return Files.exists(p) ? Optional.of(p.toUri()) : Optional.empty(); - } - + @SuppressWarnings("removal") @Override public Object instance() { return this.modContainer.getMod(); } - @Override - public Optional injector() { - return Optional.ofNullable(this.injector); - } - - public void setInjector(final Injector injector) { - this.injector = injector; - } - - private static final Map containers = new MapMaker().weakKeys().makeMap(); + private static final Map mods = new MapMaker().weakKeys().makeMap(); - public static ForgePluginContainer of(final ModContainer modContainer) { - return containers.computeIfAbsent(modContainer, ForgePluginContainer::new); + public static Optional of(final ModContainer modContainer) { + if (modContainer instanceof PluginModContainer plugin) { + return plugin.container(); + } + return Optional.of(ForgePluginContainer.mods.computeIfAbsent(modContainer, ForgePluginContainer::new)); } } diff --git a/forge/src/main/java/org/spongepowered/forge/launch/plugin/ForgePluginManager.java b/forge/src/main/java/org/spongepowered/forge/launch/plugin/ForgePluginManager.java index f3b3d09f01d..2cc74de8521 100644 --- a/forge/src/main/java/org/spongepowered/forge/launch/plugin/ForgePluginManager.java +++ b/forge/src/main/java/org/spongepowered/forge/launch/plugin/ForgePluginManager.java @@ -35,28 +35,28 @@ import java.util.Optional; @Singleton -@SuppressWarnings("unchecked") public final class ForgePluginManager implements SpongePluginManager { + @SuppressWarnings("removal") @Override public Optional fromInstance(final Object instance) { - return ModList.get().getModContainerByObject(Objects.requireNonNull(instance, "instance")).map(ForgePluginContainer::of); + return ModList.getModContainerByObject(Objects.requireNonNull(instance, "instance")).flatMap(ForgePluginContainer::of); } @Override public Optional plugin(final String id) { - return ModList.get().getModContainerById(Objects.requireNonNull(id, "id")).map(ForgePluginContainer::of); + return ModList.getModContainerById(Objects.requireNonNull(id, "id")).flatMap(ForgePluginContainer::of); } @Override public Collection plugins() { final ImmutableList.Builder builder = ImmutableList.builder(); - ModList.get().forEachModInOrder(mod -> builder.add(ForgePluginContainer.of(mod))); + ModList.forEachModInOrder(mod -> ForgePluginContainer.of(mod).ifPresent(builder::add)); return builder.build(); } @Override public boolean isReady() { - return ModList.get() != null; + return ModList.size() > 0; } } diff --git a/forge/src/main/java/org/spongepowered/forge/launch/plugin/PluginMetadataConverter.java b/forge/src/main/java/org/spongepowered/forge/launch/plugin/PluginMetadataConverter.java deleted file mode 100644 index e854d3d2a2e..00000000000 --- a/forge/src/main/java/org/spongepowered/forge/launch/plugin/PluginMetadataConverter.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.launch.plugin; - -import net.minecraftforge.fml.loading.moddiscovery.ModInfo; -import net.minecraftforge.forgespi.language.IModInfo; -import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginContributor; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginDependency; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginLinks; -import org.spongepowered.plugin.metadata.model.PluginDependency; - -import java.util.ArrayList; -import java.util.List; - -public final class PluginMetadataConverter { - - @SuppressWarnings("UnstableApiUsage") - public static PluginMetadata modToPlugin(final ModInfo info) { - final StandardPluginMetadata.Builder builder = StandardPluginMetadata.builder(); - builder - .id(info.getModId()) - .name(info.getDisplayName()) - .version(info.getVersion().toString()) - .description(info.getDescription()) - .entrypoint("unknown") - .addContributor(StandardPluginContributor.builder().name(info.getConfigElement("authors").orElse("unknown").toString()).build()) - .links(StandardPluginLinks.builder().issues(info.getOwningFile().getIssueURL()).build()) - .properties(info.getModProperties()); - - final List dependencies = new ArrayList<>(); - for (final IModInfo.ModVersion dependency : info.getDependencies()) { - final StandardPluginDependency.Builder depBuilder = StandardPluginDependency.builder(); - depBuilder - .id(dependency.getModId()) - .loadOrder(PluginMetadataConverter.orderingToLoad(dependency.getOrdering())) - .version(dependency.getVersionRange().toString()); - - dependencies.add(depBuilder.build()); - } - - builder.dependencies(dependencies); - return builder.build(); - } - - private static PluginDependency.LoadOrder orderingToLoad(final IModInfo.Ordering ordering) { - return switch (ordering) { - case BEFORE -> PluginDependency.LoadOrder.BEFORE; - case AFTER -> PluginDependency.LoadOrder.AFTER; - case NONE -> PluginDependency.LoadOrder.UNDEFINED; - }; - } -} diff --git a/forge/src/main/java/org/spongepowered/forge/launch/plugin/PluginModContainer.java b/forge/src/main/java/org/spongepowered/forge/launch/plugin/PluginModContainer.java index d0c79667637..757083feb66 100644 --- a/forge/src/main/java/org/spongepowered/forge/launch/plugin/PluginModContainer.java +++ b/forge/src/main/java/org/spongepowered/forge/launch/plugin/PluginModContainer.java @@ -24,122 +24,121 @@ */ package org.spongepowered.forge.launch.plugin; -import com.google.inject.Injector; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.eventbus.EventBusErrorMessage; -import net.minecraftforge.eventbus.api.BusBuilder; -import net.minecraftforge.eventbus.api.Event; -import net.minecraftforge.eventbus.api.IEventBus; -import net.minecraftforge.eventbus.api.IEventListener; +import net.minecraftforge.eventbus.api.bus.BusGroup; +import net.minecraftforge.eventbus.api.bus.EventBus; import net.minecraftforge.fml.Logging; import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModLoadingException; import net.minecraftforge.fml.ModLoadingStage; +import net.minecraftforge.fml.config.IConfigEvent; import net.minecraftforge.fml.event.IModBusEvent; import net.minecraftforge.fml.javafmlmod.AutomaticEventSubscriber; +import net.minecraftforge.forgespi.language.IModFileInfo; import net.minecraftforge.forgespi.language.IModInfo; import net.minecraftforge.forgespi.language.ModFileScanData; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.spongepowered.common.inject.plugin.PluginGuice; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spongepowered.common.launch.Launch; -import org.spongepowered.forge.launch.event.ForgeEventManager; +import org.spongepowered.common.launch.plugin.loader.PluginCandidate; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.plugin.metadata.model.PluginDependency; import java.util.Optional; import java.util.concurrent.CountDownLatch; -// Spongified FMLModContainer public final class PluginModContainer extends ModContainer { private static final Logger LOGGER = LogManager.getLogger(); - private final ModFileScanData scanResults; - private final IEventBus eventBus; - private Object modInstance; - private final Class modClass; + private final PluginCandidate candidate; + private final Module module; + + private final ModFileScanData scanData; + private final BusGroup busGroup; private final CountDownLatch initializationLock; - public PluginModContainer(IModInfo info, String className, ModFileScanData modFileScanResults, ModuleLayer gameLayer) { + private @MonotonicNonNull PluginContainer container; + + public PluginModContainer(final IModInfo info, final ModFileScanData scanData, final ModuleLayer layer) { super(info); - LOGGER.debug(Logging.LOADING, "Creating PluginModContainer instance for {}", className); - this.scanResults = modFileScanResults; + LOGGER.debug(Logging.LOADING, "Creating PluginModContainer instance for {}", info.getModId()); + this.scanData = scanData; this.activityMap.put(ModLoadingStage.CONSTRUCT, this::constructPlugin); - this.eventBus = BusBuilder.builder().setExceptionHandler(this::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).useModLauncher().build(); - this.configHandler = Optional.of(ce -> this.eventBus.post(ce.self())); + this.busGroup = BusGroup.create("modBusFor" + info.getModId()); this.contextExtension = () -> null; this.initializationLock = new CountDownLatch(1); - try { - Module module = gameLayer.findModule(info.getOwningFile().moduleName()).orElseThrow(); - modClass = Class.forName(module, className); - LOGGER.trace(Logging.LOADING, "Loaded modclass {} with {}", modClass.getName(), modClass.getClassLoader()); - } catch (Throwable e) { - LOGGER.error(Logging.LOADING, "Failed to load class {}", className, e); - throw new ModLoadingException(info, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmodclass", e); - } + final PluginResource resource = info.getOwningFile().getConfig().getConfigElement("spongeResource").get(); + final PluginMetadata metadata = info.getConfig().getConfigElement("spongeMetadata").get(); + this.candidate = Launch.instance().pluginFactory().create(resource, metadata); + this.module = layer.findModule(PluginModContainer.getModuleName(info.getOwningFile())).orElseThrow(); } - private void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable) { - LOGGER.error(new EventBusErrorMessage(event, i, iEventListeners, throwable)); + private static String getModuleName(final IModFileInfo fileInfo) { + return fileInfo.getMods().isEmpty() ? fileInfo.getFile().getSecureJar().name() : fileInfo.getMods().getFirst().getModId(); } private void constructPlugin() { try { - LOGGER.trace(Logging.LOADING, "Loading plugin instance {} of type {}", getModId(), this.modClass.getName()); + LOGGER.trace(Logging.LOADING, "Loading plugin container {}", getModId()); - final ForgePluginContainer pluginContainer = ForgePluginContainer.of(this); - pluginContainer.metadata().dependencies().stream() + this.candidate.metadata().dependencies().stream() .filter(d -> d.loadOrder() == PluginDependency.LoadOrder.AFTER) - .flatMap(d -> ModList.get().getModContainerById(d.id()).stream()) + .flatMap(d -> ModList.getModContainerById(d.id()).stream()) .filter(m -> m instanceof PluginModContainer) .forEach(m -> ((PluginModContainer) m).waitForInitialization()); - ModList.get().forEachModInOrder(m -> { - if (m instanceof PluginModContainer p && ForgePluginContainer.of(m).metadata().dependencies().stream() + ModList.forEachModInOrder(m -> { + if (m instanceof PluginModContainer p && p.candidate.metadata().dependencies().stream() .anyMatch(d -> d.id().equals(this.getModId()) && d.loadOrder() == PluginDependency.LoadOrder.BEFORE)) { p.waitForInitialization(); } }); - final Injector pluginInjector = PluginGuice.create(pluginContainer, this.modClass, Launch.instance().lifecycle().platformInjector()); - this.modInstance = pluginInjector.getInstance(this.modClass); - pluginContainer.setInjector(pluginInjector); - ((ForgeEventManager) MinecraftForge.EVENT_BUS).registerListeners(pluginContainer, this.modInstance); - - LOGGER.trace(Logging.LOADING, "Loaded plugin instance {} of type {}", getModId(), this.modClass.getName()); + this.container = this.candidate.load(); + LOGGER.trace(Logging.LOADING, "Loaded plugin container {}", getModId()); this.initializationLock.countDown(); - } catch (Throwable e) { - LOGGER.error(Logging.LOADING, "Failed to create plugin instance. PluginID: {}, class {}", getModId(), this.modClass.getName(), e); - throw new ModLoadingException(this.modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, this.modClass); + } catch (Exception e) { + LOGGER.error(Logging.LOADING, "Failed to create plugin container {}.", getModId(), e); + throw new ModLoadingException(this.modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e); } try { LOGGER.trace(Logging.LOADING, "Injecting Automatic event subscribers for {}", getModId()); - AutomaticEventSubscriber.inject(this, this.scanResults, this.modClass.getClassLoader()); + AutomaticEventSubscriber.inject(this, this.scanData, this.module.getClassLoader()); LOGGER.trace(Logging.LOADING, "Completed Automatic event subscribers for {}", getModId()); } catch (Throwable e) { - LOGGER.error(Logging.LOADING, "Failed to register automatic subscribers. PluginID: {}, class {}", getModId(), this.modClass.getName(), e); - throw new ModLoadingException(this.modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, this.modClass); + LOGGER.error(Logging.LOADING, "Failed to register automatic subscribers. ModID: {}", getModId(), e); + throw new ModLoadingException(this.modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e); } } @Override public boolean matches(Object mod) { - return mod == this.modInstance; + return mod == this.getMod(); } + @SuppressWarnings("removal") @Override public Object getMod() { - return this.modInstance; + return this.container.instance(); } @Override - protected void acceptEvent(final T e) { + public BusGroup getModBusGroup() { + return this.busGroup; + } + + @SuppressWarnings("unchecked") + @Override + protected void acceptEvent(final T e) { try { LOGGER.trace(Logging.LOADING, "Firing event for modid {} : {}", this.getModId(), e); - this.eventBus.post(e); + IModBusEvent.getBus(this.busGroup, (Class) e.getClass()).post(e); LOGGER.trace(Logging.LOADING, "Fired event for modid {} : {}", this.getModId(), e); } catch (Throwable t) { LOGGER.error(Logging.LOADING, "Caught exception during event {} dispatch for modid {}", e, this.getModId(), t); @@ -147,6 +146,15 @@ protected void acceptEvent(final T e) { } } + @Override + public void dispatchConfigEvent(final IConfigEvent event) { + EventBus.create(this.busGroup, event.self().getClass()).post(event.self()); + } + + public Optional container() { + return Optional.ofNullable(this.container); + } + private void waitForInitialization() { try { this.initializationLock.await(); diff --git a/forge/src/main/resource-templates/META-INF/mods.toml b/forge/src/main/resource-templates/META-INF/mods.toml deleted file mode 100644 index b6a82ce527b..00000000000 --- a/forge/src/main/resource-templates/META-INF/mods.toml +++ /dev/null @@ -1,56 +0,0 @@ -modLoader="javafml" -loaderVersion="[52,)" -license="MIT License" - -issueTrackerURL="https://github.com/SpongePowered/Sponge/issues" - - -[[mods]] -modId="spongeforge" -version="{{ version }}" -displayName="SpongeForge" -displayTest="IGNORE_SERVER_VERSION" -credits="SpongePowered and Contributors" -authors="SpongePowered" -description = "{{ description }}" - -[[dependencies.spongeforge]] -modId="forge" -mandatory=true -versionRange="[52,)" -ordering="AFTER" -side="BOTH" - -[[dependencies.spongeforge]] -modId="sponge" -mandatory=true -versionRange="[{{ version }}]" -ordering="AFTER" -side="BOTH" - - -[[mods]] -modId="sponge" -version="{{ version }}" -displayName="Sponge" -displayTest="IGNORE_SERVER_VERSION" -credits="SpongePowered and Contributors" -authors="SpongePowered" -description="The SpongeAPI implementation targeting vanilla Minecraft and 3rd party platforms" - -[[dependencies.sponge]] -modId="spongeapi" -mandatory=true -versionRange="[12.0.0,)" -ordering="AFTER" -side="BOTH" - - -[[mods]] -modId="spongeapi" -version="12.0.0" -displayName="SpongeAPI" -displayTest="IGNORE_SERVER_VERSION" -credits="SpongePowered and Contributors" -authors="SpongePowered" -description="A Minecraft plugin API" diff --git a/forge/src/main/resource-templates/META-INF/sponge_plugins.json b/forge/src/main/resource-templates/META-INF/sponge_plugins.json new file mode 100644 index 00000000000..5ce964326cf --- /dev/null +++ b/forge/src/main/resource-templates/META-INF/sponge_plugins.json @@ -0,0 +1,57 @@ +{ + "global": { + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "MIT", + "links": { + "homepage": "https://www.spongepowered.org", + "source": "https://www.spongepowered.org/source", + "issues": "https://www.spongepowered.org/issues" + }, + "contributors": [ + { + "name": "SpongePowered", + "description": "Lead Developer" + } + ] + }, + "plugins": [ + { + "id": "spongeforge", + "name": "SpongeForge", + "version": "{{ version }}", + "entrypoints": ["org.spongepowered.forge.SpongeForge"], + "description": "{{ description }}", + "dependencies": [ + { + "id": "forge", + "version": "[{{ forgeVersion }},)" + }, + { + "id": "sponge", + "version": "{{ version }}" + } + ] + }, + { + "id": "sponge", + "name": "Sponge", + "version": "{{ version }}", + "description": "The common Sponge implementation", + "dependencies": [ + { + "id": "spongeapi", + "version": "{{ apiVersion }}" + } + ] + }, + { + "id": "spongeapi", + "name": "SpongeAPI", + "version": "{{ apiVersion }}", + "description": "The Minecraft API specification" + } + ] +} \ No newline at end of file diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/block/ChangeBlockEvent_AllMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/block/ChangeBlockEvent_AllMixin_Forge.java deleted file mode 100644 index d9ad83e55f4..00000000000 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/block/ChangeBlockEvent_AllMixin_Forge.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.mixin.core.api.event.block; - -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.event.level.BlockEvent; -import net.minecraftforge.eventbus.api.Event; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.block.transaction.BlockTransaction; -import org.spongepowered.api.block.transaction.Operation; -import org.spongepowered.api.block.transaction.Operations; -import org.spongepowered.api.event.block.ChangeBlockEvent; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.common.util.VecHelper; -import org.spongepowered.forge.launch.bridge.event.SpongeEventBridge_Forge; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -@Mixin(value = ChangeBlockEvent.All.class, remap = false) -public interface ChangeBlockEvent_AllMixin_Forge extends SpongeEventBridge_Forge { - - @Override - default @Nullable Collection bridge$createForgeEvents() { - final ChangeBlockEvent.All thisEvent = ((ChangeBlockEvent.All) this); - final Player player = thisEvent.cause().first(Player.class).orElse(null); - final Operation breakOp = Operations.BREAK.get(); - - final List forgeEvents = new ArrayList<>(); - for (final BlockTransaction transaction : thisEvent.transactions()) { - if (player != null && transaction.operation() == breakOp) { - forgeEvents.add(new BlockEvent.BreakEvent((Level) thisEvent.world(), - VecHelper.toBlockPos(transaction.original().position()), - (BlockState) transaction.original().state(), player)); - } - // TODO: Other event types may go here - break is a PoC. - } - return forgeEvents; - } - -} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/entity/ChangeEventWorldEvent_PostMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/entity/ChangeEventWorldEvent_PostMixin_Forge.java deleted file mode 100644 index f85995d0727..00000000000 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/entity/ChangeEventWorldEvent_PostMixin_Forge.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.mixin.core.api.event.entity; - -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.event.entity.player.PlayerEvent; -import net.minecraftforge.eventbus.api.Event; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.forge.mixin.core.api.event.EventMixin_Forge; - -import java.util.Collection; -import java.util.Collections; - -@Mixin(ChangeEntityWorldEvent.Post.class) -public interface ChangeEventWorldEvent_PostMixin_Forge extends EventMixin_Forge { - - @Override - default @Nullable Collection bridge$createForgeEvents() { - if (((ChangeEntityWorldEvent.Post) this).entity() instanceof ServerPlayer) { - final ServerPlayer player = (ServerPlayer) ((ChangeEntityWorldEvent.Post) this).entity(); - - return Collections.singletonList(new PlayerEvent.PlayerChangedDimensionEvent(player, - ((ServerLevel) ((ChangeEntityWorldEvent.Post) this).originalWorld()).dimension(), - ((ServerLevel) ((ChangeEntityWorldEvent.Post) this).destinationWorld()).dimension())); - } - return null; - } -} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/commands/CommandsMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/commands/CommandsMixin_Forge.java index f5c9348ba99..d8bb352a0f5 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/commands/CommandsMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/commands/CommandsMixin_Forge.java @@ -29,11 +29,9 @@ import com.mojang.brigadier.tree.CommandNode; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.eventbus.api.Event; -import net.minecraftforge.eventbus.api.IEventBus; -import net.minecraftforge.server.command.CommandHelper; +import net.minecraftforge.eventbus.api.bus.CancellableEventBus; +import net.minecraftforge.eventbus.internal.Event; import org.spongepowered.api.command.CommandCause; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; @@ -46,57 +44,48 @@ import org.spongepowered.common.bridge.commands.CommandSourceStackBridge; import org.spongepowered.common.bridge.commands.CommandsBridge; import org.spongepowered.common.command.manager.SpongeCommandManager; +import org.spongepowered.common.command.resolver.SpongeSuggestionTreeResolver; import org.spongepowered.common.event.tracking.PhaseTracker; import java.util.IdentityHashMap; -import java.util.List; import java.util.Map; -import java.util.WeakHashMap; import java.util.function.Function; @Mixin(Commands.class) public abstract class CommandsMixin_Forge implements CommandsBridge { - private WeakHashMap, List>>> impl$playerNodeCache; private SpongeCommandManager impl$commandManager; // The event fired by Forge is fired in ForgeCommandManager at the appropriate time. @Redirect(method = "performCommand", - at = @At(value = "INVOKE", target = "Lnet/minecraftforge/eventbus/api/IEventBus;post(Lnet/minecraftforge/eventbus/api/Event;)Z")) - private boolean forge$redirectToSpongeCommandManager(IEventBus instance, Event event) { + at = @At(value = "INVOKE", target = "Lnet/minecraftforge/eventbus/api/bus/CancellableEventBus;post(Lnet/minecraftforge/eventbus/internal/Event;)Z")) + private boolean forge$redirectToSpongeCommandManager(CancellableEventBus instance, Event event) { return false; } - @SuppressWarnings("unchecked") - @Redirect(method = "sendCommands", at = @At( - value = "INVOKE", - target = "Lnet/minecraftforge/server/command/CommandHelper;mergeCommandNode(Lcom/mojang/brigadier/tree/CommandNode;Lcom/mojang/brigadier/tree/CommandNode;Ljava/util/Map;Ljava/lang/Object;Lcom/mojang/brigadier/Command;Ljava/util/function/Function;)V", - remap = false - )) + @Redirect(method = "sendCommands", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/server/command/CommandHelper;mergeCommandNode(Lcom/mojang/brigadier/tree/CommandNode;Lcom/mojang/brigadier/tree/CommandNode;Ljava/util/Map;Ljava/lang/Object;Lcom/mojang/brigadier/Command;Ljava/util/function/Function;)V")) private void impl$addNonBrigSuggestions( - final CommandNode sourceChild, - final CommandNode sourceNode, - final Map, CommandNode> resultNode, - final S sourceToResult, - final Command canUse, - final Function, SuggestionProvider> execute, + final CommandNode rootCommandNode, + final CommandNode rootSuggestion, + final Map, CommandNode> commandToSuggestion, + final S source, + final Command execute, + final Function, SuggestionProvider> commandToSuggestionFunction, final ServerPlayer player ) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(player); frame.addContext(EventContextKeys.SUBJECT, (Subject) player); - final CommandCause sourceToUse = ((CommandSourceStackBridge) sourceToResult).bridge$withCurrentCause(); - try { - this.impl$playerNodeCache.put(player, new IdentityHashMap<>()); - // We use this because the redirects should be a 1:1 mapping (which is what this map is for). - final IdentityHashMap, CommandNode> idMap = new IdentityHashMap<>(resultNode); - CommandHelper.mergeCommandNode(sourceChild, sourceNode, idMap, sourceToResult, canUse, execute); - } finally { - this.impl$playerNodeCache.remove(player); - } - for (final CommandNode node : this.impl$commandManager.getNonBrigadierSuggestions(sourceToUse)) { - sourceNode.addChild((CommandNode) node); + final CommandCause sourceToUse = ((CommandSourceStackBridge) source).bridge$withCurrentCause(); + + // We use this because the redirects should be a 1:1 mapping (which is what this map is for). + final IdentityHashMap, CommandNode> idMap = new IdentityHashMap(commandToSuggestion); + new SpongeSuggestionTreeResolver((CommandSourceStack) sourceToUse, idMap, new IdentityHashMap<>(), this.impl$commandManager) + .fillSuggestions((CommandNode) rootCommandNode, (CommandNode) rootSuggestion); + + for (final CommandNode node : this.impl$commandManager.getNonBrigadierSuggestions(sourceToUse)) { + rootSuggestion.addChild((CommandNode) node); } } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/ForgeHooksMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/ForgeHooksMixin_Forge.java new file mode 100644 index 00000000000..a6e7c4467da --- /dev/null +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/ForgeHooksMixin_Forge.java @@ -0,0 +1,63 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.mixin.core.minecraftforge; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.loot.LootModifierManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.Collections; + +@Mixin(ForgeHooks.class) +public abstract class ForgeHooksMixin_Forge { + + // Forge's LootModifierManager is initialized during resource reload (AddReloadListenerEvent). + // On an integrated server's first tick, resources may not have loaded yet, + // causing ForgeInternalHandler.getLootModifierManager() to throw ISE. + // Guard by wrapping the call and returning an empty modifier list if unavailable. + @WrapOperation(method = "modifyLoot", at = @At(value = "INVOKE", + target = "Lnet/minecraftforge/common/ForgeInternalHandler;getLootModifierManager()Lnet/minecraftforge/common/loot/LootModifierManager;", + remap = false), remap = false) + private static LootModifierManager forge$guardLootModifierManager(final Operation original) { + try { + return original.call(); + } catch (final IllegalStateException e) { + return null; + } + } + + @WrapOperation(method = "modifyLoot", at = @At(value = "INVOKE", + target = "Lnet/minecraftforge/common/loot/LootModifierManager;getAllLootMods()Ljava/util/Collection;", + remap = false), remap = false) + private static java.util.Collection forge$guardGetAllLootMods(final LootModifierManager instance, final Operation> original) { + if (instance == null) { + return Collections.emptyList(); + } + return original.call(instance); + } +} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/MinecraftForgeMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/MinecraftForgeMixin_Forge.java index 1f23d293092..da6df27b827 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/MinecraftForgeMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/MinecraftForgeMixin_Forge.java @@ -25,22 +25,10 @@ package org.spongepowered.forge.mixin.core.minecraftforge; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.eventbus.BusBuilderImpl; -import net.minecraftforge.eventbus.api.BusBuilder; -import net.minecraftforge.eventbus.api.IEventBus; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.forge.launch.event.ForgeEventManager; -import org.spongepowered.forge.launch.event.SpongeEventBus; @Mixin(MinecraftForge.class) public abstract class MinecraftForgeMixin_Forge { - @Redirect(method = "", at = @At(value = "INVOKE", - target = "Lnet/minecraftforge/eventbus/api/BusBuilder;build()Lnet/minecraftforge/eventbus/api/IEventBus;", remap = false)) - private static IEventBus forge$substituteForgeEventBus(final BusBuilder builder) { - return new ForgeEventManager(new SpongeEventBus((BusBuilderImpl) builder)); - } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/EntityTravelToDimensionEventMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/EntityTravelToDimensionEventMixin_Forge.java deleted file mode 100644 index 6c68bbcd05e..00000000000 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/EntityTravelToDimensionEventMixin_Forge.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.mixin.core.minecraftforge.event.entity; - -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Level; -import net.minecraftforge.event.entity.EntityEvent; -import net.minecraftforge.event.entity.EntityTravelToDimensionEvent; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; - -@Mixin(value = EntityTravelToDimensionEvent.class, remap = false) -public abstract class EntityTravelToDimensionEventMixin_Forge extends EntityEvent implements ForgeEventBridge_Forge { - - // @formatter:off - @Shadow @Final @Mutable private ResourceKey dimension; - // @formatter:on - - private EntityTravelToDimensionEventMixin_Forge(Entity entity) { - super(entity); - } - - @Override - public void bridge$syncFrom(final ChangeEntityWorldEvent.Pre event) { - this.setCanceled(event.isCancelled()); - this.dimension = ((ServerLevel) event.destinationWorld()).dimension(); - } - - @Override - public void bridge$syncTo(final ChangeEntityWorldEvent.Pre event) { - event.setCancelled(this.isCanceled()); - } - - @Override - public ChangeEntityWorldEvent.@Nullable Pre bridge$createSpongeEvent() { - final Entity entity = this.getEntity(); - final ServerLevel toWorld = SpongeCommon.server().getLevel(this.dimension); - return SpongeEventFactory.createChangeEntityWorldEventPre(PhaseTracker.getInstance().currentCause(), - (org.spongepowered.api.entity.Entity) entity, (org.spongepowered.api.world.server.ServerWorld) entity.getCommandSenderWorld(), - (org.spongepowered.api.world.server.ServerWorld) toWorld, (org.spongepowered.api.world.server.ServerWorld) toWorld); - } - -} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/ItemTossEventMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/ItemTossEventMixin_Forge.java deleted file mode 100644 index 105a97f7b67..00000000000 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/ItemTossEventMixin_Forge.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.mixin.core.minecraftforge.event.entity; - -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraftforge.event.entity.item.ItemEvent; -import net.minecraftforge.event.entity.item.ItemTossEvent; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.event.EventContextKeys; -import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.cause.entity.SpawnTypes; -import org.spongepowered.api.event.item.inventory.DropItemEvent; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; - -import java.util.ArrayList; - -@Mixin(ItemTossEvent.class) -public abstract class ItemTossEventMixin_Forge extends ItemEvent implements ForgeEventBridge_Forge { - - //@formatter:off - @Shadow private @Final Player player; - //@formatter:on - - private ItemTossEventMixin_Forge(ItemEntity itemEntity) { - super(itemEntity); - } - - @Override - public void bridge$syncFrom(DropItemEvent.Dispense event) { - event.entities().stream() - .filter(e -> e instanceof ItemEntity) - .map(e -> (ItemEntity) e) - .findFirst() - .ifPresent(e -> this.getEntity().setItem(e.getItem())); - this.setCanceled(event.isCancelled()); - } - - @Override - public void bridge$syncTo(DropItemEvent.Dispense event) { - event.setCancelled(this.isCanceled()); - } - - @Override - public DropItemEvent.@Nullable Dispense bridge$createSpongeEvent() { - if (this.player.level().isClientSide()) { - return null; - } - try (final var frame = PhaseTracker.getInstance().pushCauseFrame()) { - frame.addContext(EventContextKeys.SPAWN_TYPE, SpawnTypes.DROPPED_ITEM); - final var cause = frame.currentCause(); - final var toSpawn = (Entity) this.getEntity(); - final var list = new ArrayList(); - list.add(toSpawn); - return SpongeEventFactory.createDropItemEventDispense(cause, list); - } - } -} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/player/PlayerEvent_PlayerChangedDimensionEventMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/player/PlayerEvent_PlayerChangedDimensionEventMixin_Forge.java deleted file mode 100644 index 27d38facc3e..00000000000 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/player/PlayerEvent_PlayerChangedDimensionEventMixin_Forge.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.mixin.core.minecraftforge.event.entity.player; - -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.Level; -import net.minecraftforge.event.entity.player.PlayerEvent; -import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; -import org.spongepowered.api.world.server.ServerWorld; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; - -@Mixin(value = PlayerEvent.PlayerChangedDimensionEvent.class, remap = false) -public final class PlayerEvent_PlayerChangedDimensionEventMixin_Forge implements ForgeEventBridge_Forge{ - - // @formatter:off - @Shadow @Final private ResourceKey fromDim; - @Shadow @Final private ResourceKey toDim; - // @formatter:on - - @Override - public void bridge$syncFrom(final ChangeEntityWorldEvent.Post event) { - // nothing to do -- informational only - } - - @Override - public void bridge$syncTo(final ChangeEntityWorldEvent.Post event) { - // nothing to do -- informational only - } - - @SuppressWarnings("ConstantConditions") - @Override - public ChangeEntityWorldEvent.Post bridge$createSpongeEvent() { - final PlayerEvent.PlayerChangedDimensionEvent thisEvent = (PlayerEvent.PlayerChangedDimensionEvent) (Object) this; - return SpongeEventFactory.createChangeEntityWorldEventPost( - PhaseTracker.getInstance().currentCause(), - (Entity) thisEvent.getEntity(), - (ServerWorld) SpongeCommon.server().getLevel(this.fromDim), - (ServerWorld) SpongeCommon.server().getLevel(this.toDim), - (ServerWorld) SpongeCommon.server().getLevel(this.toDim) - ); - } - -} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEvent_BreakEventMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEvent_BreakEventMixin_Forge.java deleted file mode 100644 index eb378c518ac..00000000000 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEvent_BreakEventMixin_Forge.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.mixin.core.minecraftforge.event.world; - -import net.minecraft.world.level.LevelAccessor; -import net.minecraftforge.event.level.BlockEvent; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.block.BlockState; -import org.spongepowered.api.block.BlockTypes; -import org.spongepowered.api.block.transaction.BlockTransaction; -import org.spongepowered.api.block.transaction.Operations; -import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.block.ChangeBlockEvent; -import org.spongepowered.api.world.server.ServerWorld; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.common.block.SpongeBlockSnapshot; -import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.util.VecHelper; -import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; -import org.spongepowered.math.vector.Vector3i; - -import java.util.Collections; - -@Mixin(value = BlockEvent.BreakEvent.class, remap = false) -public abstract class BlockEvent_BreakEventMixin_Forge extends BlockEventMixin_Forge implements ForgeEventBridge_Forge { - - @Override - public void bridge$syncFrom(final ChangeBlockEvent.All event) { - final Vector3i pos = VecHelper.toVector3i(this.shadow$getPos()); - if (event.isCancelled() || - event.transactions() - .stream() - .filter(x -> x.original().position().equals(pos)) - .anyMatch(x -> !x.isValid() || x.operation() != Operations.BREAK.get() || x.custom().isPresent())) { - this.setCanceled(true); - } - } - - @Override - public void bridge$syncTo(final ChangeBlockEvent.All event) { - if (this.isCanceled()) { - final Vector3i pos = VecHelper.toVector3i(this.shadow$getPos()); - event.transactions(Operations.BREAK.get()).filter(x -> x.original().position().equals(pos)) - .forEach(x -> x.setValid(false)); - } - } - - @Override - public ChangeBlockEvent.@Nullable All bridge$createSpongeEvent() { - final LevelAccessor accessor = this.shadow$getLevel(); - if (accessor instanceof ServerWorld serverWorld) { - final BlockTransaction transaction = new BlockTransaction( - SpongeBlockSnapshot.BuilderImpl - .pooled() - .world(serverWorld.key()) - .position(VecHelper.toVector3i(this.shadow$getPos())) - .blockState((BlockState) this.shadow$getState()) - .build(), - SpongeBlockSnapshot.BuilderImpl - .pooled() - .world(serverWorld.key()) - .position(VecHelper.toVector3i(this.shadow$getPos())) - .blockState(BlockState.builder().blockType(BlockTypes.AIR.get()).build()) - .build(), - Operations.BREAK.get() - ); - return SpongeEventFactory.createChangeBlockEventAll(PhaseTracker.getInstance().currentCause(), - Collections.singletonList(transaction), serverWorld); - } - return null; - } -} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/ForgeRegistryMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/ForgeRegistryMixin_Forge.java index d382d4bd688..7d447c466e1 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/ForgeRegistryMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/ForgeRegistryMixin_Forge.java @@ -26,7 +26,7 @@ import com.google.common.collect.Maps; import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraftforge.registries.ForgeRegistry; import net.minecraftforge.registries.IForgeRegistry; import org.spongepowered.api.ResourceKey; @@ -55,10 +55,10 @@ public abstract class ForgeRegistryMixin_Forge { private final Map> forge$parents = Maps.newHashMap(); private boolean forge$warnedIfNoParent; - @Inject(method = "add(ILnet/minecraft/resources/ResourceLocation;Ljava/lang/Object;Ljava/lang/String;)I", at = @At("TAIL")) - public void forge$writeToParent(final int id, final ResourceLocation key, final V value, final String owner, final CallbackInfoReturnable cir) { + @Inject(method = "add(ILnet/minecraft/resources/Identifier;Ljava/lang/Object;Ljava/lang/String;)I", at = @At("TAIL")) + public void forge$writeToParent(final int id, final Identifier key, final V value, final String owner, final CallbackInfoReturnable cir) { final ResourceKey root = (ResourceKey) (Object) this.key.registry(); - final ResourceKey location = (ResourceKey) (Object) this.key.location(); + final var location = (ResourceKey) (Object) this.key.identifier(); if (!this.forge$warnedIfNoParent && this.forge$parents.isEmpty()) { // We only care about minecraft namespaced registries, as that is what we've got parents for. diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/GameDataMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/GameDataMixin_Forge.java index d2b9406b895..42cf7c3f0f4 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/GameDataMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/GameDataMixin_Forge.java @@ -30,19 +30,48 @@ import net.minecraft.world.entity.EntityType; import net.minecraftforge.registries.GameData; import org.jetbrains.annotations.Nullable; +import org.spongepowered.api.Sponge; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.entity.SpongeEntityTypes; +import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.launch.Lifecycle; +import org.spongepowered.common.network.channel.SpongeChannelManager; +import org.spongepowered.common.network.packet.SpongePacketHandler; @Mixin(GameData.class) public class GameDataMixin_Forge { - @Inject(method = "postRegisterEvents", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/ModLoader;postEventWrapContainerInModOrder(Lnet/minecraftforge/eventbus/api/Event;)V", shift = At.Shift.AFTER)) + @Inject(method = "postRegisterEvents", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/ModLoader;postEventWrapContainerInModOrder(Lnet/minecraftforge/fml/event/IModBusEvent;)V", shift = At.Shift.AFTER)) private static void forge$registerSpongeTypesLast(final CallbackInfo ci, @Nullable @Local final Registry vanillaRegistry) { if (vanillaRegistry != null && Registries.ENTITY_TYPE.equals(vanillaRegistry.key())) { SpongeEntityTypes.register((Registry>) vanillaRegistry); } } + + @Inject(method = "postRegisterEvents", at = @At("HEAD")) + private static void forge$onRegisterEvents(final CallbackInfo ci) { + final Lifecycle lifecycle = Launch.instance().lifecycle(); + lifecycle.callConstructEvent(); + lifecycle.callRegisterFactoryEvent(); + lifecycle.callRegisterBuilderEvent(); + lifecycle.callRegisterChannelEvent(); + lifecycle.establishGameServices(); + lifecycle.establishDataKeyListeners(); + + SpongePacketHandler.init((SpongeChannelManager) Sponge.channelManager()); + + lifecycle.establishDataProviders(); + lifecycle.callRegisterDataEvent(); + lifecycle.establishEarlyGlobalRegistries(); + + Launch.instance().lifecycle().establishGlobalRegistries(); + } + + @Inject(method = "freezeData", at = @At("TAIL")) + private static void forge$onFreezeData(final CallbackInfo ci) { + Launch.instance().lifecycle().endEstablishGlobalRegistries(); + } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEventMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/NamespacedWrapperMixin_Forge.java similarity index 63% rename from forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEventMixin_Forge.java rename to forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/NamespacedWrapperMixin_Forge.java index a833bfbcbb1..0a2d34bd4a5 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEventMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/NamespacedWrapperMixin_Forge.java @@ -22,23 +22,28 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.mixin.core.minecraftforge.event.world; +package org.spongepowered.forge.mixin.core.minecraftforge.registries; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.event.level.BlockEvent; -import net.minecraftforge.eventbus.api.Event; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.accessor.core.MappedRegistryAccessor; -@Mixin(value = BlockEvent.class, remap = false) -public abstract class BlockEventMixin_Forge extends Event { +/** + * Due to the Accessor being intrinsically implemented on top of MappedRegistry, the + * extended NamespacedWrapper class redefines frozen as a private field, leaving the + * original accessor to be useless. To fix registry issues, we therefore re-implement + * our accessor interface explicitly for registry purposes. + * + * @author gabizou + * @param + */ +@Mixin(targets = "net/minecraftforge/registries/NamespacedWrapper") +public abstract class NamespacedWrapperMixin_Forge implements MappedRegistryAccessor { - // @formatter:off - @Shadow public abstract LevelAccessor shadow$getLevel(); - @Shadow public abstract BlockPos shadow$getPos(); - @Shadow public abstract BlockState shadow$getState(); - // @formatter:on + @Shadow private boolean frozen; + @Override + public boolean accessor$frozen() { + return this.frozen; + } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/RegistryManagerMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/RegistryManagerMixin_Forge.java index f591b0f1463..dfa9d45f21e 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/RegistryManagerMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/registries/RegistryManagerMixin_Forge.java @@ -24,7 +24,7 @@ */ package org.spongepowered.forge.mixin.core.minecraftforge.registries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraftforge.registries.ForgeRegistry; import net.minecraftforge.registries.RegistryManager; import org.spongepowered.asm.mixin.Mixin; @@ -39,7 +39,7 @@ public class RegistryManagerMixin_Forge { @SuppressWarnings("UnstableApiUsage") @Inject(method = "takeSnapshot", at = @At("RETURN")) - public void forge$dontSendSpongeDataToClient(boolean savingToDisc, CallbackInfoReturnable> cir) { + public void forge$dontSendSpongeDataToClient(boolean savingToDisc, CallbackInfoReturnable> cir) { if (savingToDisc) { return; } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/ConnectionMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/ConnectionMixin_Forge.java index ed066933318..afa83675b9c 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/ConnectionMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/ConnectionMixin_Forge.java @@ -24,23 +24,40 @@ */ package org.spongepowered.forge.mixin.core.network; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; import net.minecraft.network.Connection; -import net.minecraft.network.PacketSendListener; +import net.minecraft.network.protocol.Packet; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.network.channel.PacketSender; @Mixin(Connection.class) public abstract class ConnectionMixin_Forge { - @Inject(method = "lambda$doSendPacket$15", at = @At(value = "INVOKE", target = "Lio/netty/util/concurrent/Future;isSuccess()Z")) - public void impl$onPacketSent(final PacketSendListener listener, final Future future, final CallbackInfo ci) { - if (listener instanceof final PacketSender.SpongePacketSendListener spongeListener) { - spongeListener.accept(future.cause()); + // Forge 26.2 adds a second addListener call (packet logger) right after this one — pin to the first. + @WrapOperation(method = "doSendPacket", at = @At( + value = "INVOKE", + target = "Lio/netty/channel/ChannelFuture;addListener(Lio/netty/util/concurrent/GenericFutureListener;)Lio/netty/channel/ChannelFuture;", + ordinal = 0, + remap = false + )) + public ChannelFuture impl$onPacketSent( + final ChannelFuture instance, final GenericFutureListener> genericFutureListener, + final Operation original, final Packet packet, + final ChannelFutureListener listener, final boolean abool + ) { + if (listener instanceof PacketSender.SpongePacketSendListener spsl) { + original.call(instance, (ChannelFutureListener) (future) -> { + if (!future.isSuccess()) { + spsl.accept(future.cause()); + } + }); } + return original.call(instance, genericFutureListener); } - } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/protocol/common/ClientboundCustomPayloadPacketMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/protocol/common/ClientboundCustomPayloadPacketMixin_Forge.java index be4df169def..6832fadbc81 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/protocol/common/ClientboundCustomPayloadPacketMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/protocol/common/ClientboundCustomPayloadPacketMixin_Forge.java @@ -28,7 +28,7 @@ import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraftforge.network.NetworkRegistry; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; @@ -49,7 +49,7 @@ public abstract class ClientboundCustomPayloadPacketMixin_Forge { // @formatter: on @Inject(method = { "lambda$static$0", "lambda$static$2" }, at = @At("HEAD"), cancellable = true) - private static void forge$getSpongeCustomPacketPayload(final ResourceLocation channelKey, final CallbackInfoReturnable> cir) { + private static void forge$getSpongeCustomPacketPayload(final Identifier channelKey, final CallbackInfoReturnable> cir) { if (NetworkRegistry.findTarget(channelKey) != null) { return; } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/protocol/common/ServerboundCustomPayloadPacketMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/protocol/common/ServerboundCustomPayloadPacketMixin_Forge.java index e5aa2980470..c58e94df499 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/protocol/common/ServerboundCustomPayloadPacketMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/network/protocol/common/ServerboundCustomPayloadPacketMixin_Forge.java @@ -28,7 +28,7 @@ import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraftforge.network.NetworkRegistry; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; @@ -49,7 +49,7 @@ public abstract class ServerboundCustomPayloadPacketMixin_Forge { // @formatter: on @Inject(method = { "lambda$static$0" }, at = @At("HEAD"), cancellable = true) - private static void forge$getSpongeCustomPacketPayload(final ResourceLocation channelKey, final CallbackInfoReturnable> cir) { + private static void forge$getSpongeCustomPacketPayload(final Identifier channelKey, final CallbackInfoReturnable> cir) { if (NetworkRegistry.findTarget(channelKey) != null) { return; } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/resources/RegistryDataLoaderMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/resources/RegistryDataLoaderMixin_Forge.java new file mode 100644 index 00000000000..d64cef16ce3 --- /dev/null +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/resources/RegistryDataLoaderMixin_Forge.java @@ -0,0 +1,124 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.mixin.core.resources; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.core.RegistryAccess; +import net.minecraft.resources.RegistryDataLoader; +import net.minecraft.resources.RegistryLoadTask; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.DependencySorter; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryRoots; +import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.accessor.resources.RegistryDataLoader_ResourceManagerRegistryLoadTaskAccessor; +import org.spongepowered.common.accessor.resources.RegistryLoadTaskAccessor; +import org.spongepowered.common.bridge.core.WritableRegistryBridge; +import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.launch.Lifecycle; +import org.spongepowered.common.registry.SpongeRegistryDependencyEntry; +import org.spongepowered.common.registry.SpongeRegistryHolder; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Mixin(RegistryDataLoader.class) +public class RegistryDataLoaderMixin_Forge { + + // Forge's lambda$load$2 is the freeze-step lambda in the async load pipeline. + // Unlike NeoForge, Forge doesn't have a gametest hook before the freeze logic, + // so we can target stream() ordinal 0 directly with no @Slice. + @SuppressWarnings({"rawtypes", "unchecked"}) + @WrapOperation(method = "lambda$load$2", + at = @At(value = "INVOKE", target = "Ljava/util/List;stream()Ljava/util/stream/Stream;", ordinal = 0)) + private static Stream impl$onLoad( + List> instance, Operation original + ) { + // TODO: Remote layer + final DependencySorter, SpongeRegistryDependencyEntry>> dependencies = new DependencySorter<>(); + final Lifecycle lifecycle = Launch.instance().lifecycle(); + final var collect = instance.stream() + .collect(Collectors.groupingBy(l -> { + if (l instanceof RegistryDataLoader_ResourceManagerRegistryLoadTaskAccessor accessor) { + return accessor.accessor$getResourceManager(); + } + return ResourceManager.Empty.INSTANCE; + }, Collectors.toSet())); + collect + .forEach((k, v) -> { + final RegistryHolder registryHolder; + if (k == ResourceManager.Empty.INSTANCE) { + // on the client side, we don't have a real resource manager + registryHolder = SpongeCommon.game(); + } else { + registryHolder = (RegistryHolder) k; + } + // Build a combined registry list: new batch registries replace any existing ones + // with the same key (e.g., on re-join, the second load produces fresh registries + // for the same keys that were already in the root from the first load). + final var batchRegistries = v.stream() + .map(RegistryLoadTaskAccessor.class::cast) + .map(RegistryLoadTaskAccessor::accessor$registry) + .toList(); + final var batchKeys = batchRegistries.stream() + .map(net.minecraft.core.Registry::key) + .collect(java.util.stream.Collectors.toSet()); + ((SpongeRegistryHolder) registryHolder).setRootMinecraftRegistry(new RegistryAccess.ImmutableRegistryAccess( + (List) Stream.concat( + registryHolder.streamRegistries(RegistryRoots.MINECRAFT) + .filter(r -> !batchKeys.contains(((net.minecraft.core.Registry) r).key())), + batchRegistries.stream() + ).toList())); + + // Only fire plugin registry events on the server side (ResourceManager-backed loads). + // Client-side network loads only receive SYNCHRONIZED_REGISTRIES and lack server-only + // registries (placed_feature, configured_feature, etc.) that plugins may declare as + // dependencies, causing freeze validation to fail. + if (k != ResourceManager.Empty.INSTANCE) { + lifecycle.processServerRegistries(registryHolder, v.stream() + .map(RegistryLoadTaskAccessor.class::cast) + .filter(l -> !RegistryDataLoader.DIMENSION_REGISTRIES.contains(l.accessor$data())) // NOTE: DIMENSION_REGISTRIES are special! + .map(l -> (Registry) l.accessor$registry())); + } + + v + .stream().map(RegistryLoadTaskAccessor.class::cast) + .forEach(l -> dependencies.addEntry(((Registry) l.accessor$registry()).type(), + new SpongeRegistryDependencyEntry<>(l, ((WritableRegistryBridge) l.accessor$registry()).bridge$pendingDependencies().toList()))); + }); + + List> loaders = new ArrayList<>(collect.size()); + dependencies.orderByDependencies(($, v) -> loaders.add(v.cookie())); + + return original.call(loaders); + } +} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/MinecraftServerMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/MinecraftServerMixin_Forge.java index d64d8c83bd5..ca9d9de9e3a 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/MinecraftServerMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/MinecraftServerMixin_Forge.java @@ -24,17 +24,29 @@ */ package org.spongepowered.forge.mixin.core.server; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.packs.resources.MultiPackResourceManager; import net.minecraft.world.level.Level; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Coerce; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.accessor.server.MinecraftServer_ReloadableResourcesAccessor; +import org.spongepowered.common.bridge.server.MinecraftServerBridge; import org.spongepowered.common.bridge.server.level.ServerLevelBridge; +import org.spongepowered.common.registry.SpongeRegistryHolder; import org.spongepowered.forge.ForgeServer; +import java.util.Collection; + @Mixin(MinecraftServer.class) public abstract class MinecraftServerMixin_Forge implements ForgeServer { @@ -50,4 +62,15 @@ public abstract class MinecraftServerMixin_Forge implements ForgeServer { public long[] getTickTime(ResourceKey dim) { return ((ServerLevelBridge) this.shadow$getLevel(dim)).bridge$recentTickTimes(); } + + @ModifyExpressionValue(method = "lambda$reloadResources$1", at = @At(value = "NEW", target = "Lnet/minecraft/server/packs/resources/MultiPackResourceManager;")) + private MultiPackResourceManager forge$onReloadResources(final MultiPackResourceManager original) { + ((MinecraftServerBridge) this).bridge$reloadServerRegistries((RegistryHolder) original); + return original; + } + + @Inject(method = "lambda$reloadResources$4", at = @At("TAIL")) + public void forge$onReloadedResources(final Collection $$0x, final @Coerce MinecraftServer_ReloadableResourcesAccessor $$1x, final CallbackInfo ci) { + ((MinecraftServerBridge) this).bridge$reloadedServerRegistries(((SpongeRegistryHolder) $$1x.accessor$resourceManager()).registryHolder()); + } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/ReloadableServerResourcesMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/ReloadableServerResourcesMixin_Forge.java new file mode 100644 index 00000000000..696e261ee09 --- /dev/null +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/ReloadableServerResourcesMixin_Forge.java @@ -0,0 +1,67 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.mixin.core.server; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.commands.Commands; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.LayeredRegistryAccess; +import net.minecraft.server.ReloadableServerResources; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.permissions.PermissionSet; +import net.minecraft.world.flag.FeatureFlagSet; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.bridge.server.packs.resources.ResourceManagerBridge; +import org.spongepowered.common.command.brigadier.dispatcher.DelegatingCommandDispatcher; + +import java.util.List; + +@Mixin(ReloadableServerResources.class) +public abstract class ReloadableServerResourcesMixin_Forge { + + @WrapOperation(method = "lambda$loadResources$2", at = @At(value = "NEW", + target = "(Lnet/minecraft/core/LayeredRegistryAccess;Lnet/minecraft/core/HolderLookup$Provider;Lnet/minecraft/world/flag/FeatureFlagSet;Lnet/minecraft/commands/Commands$CommandSelection;Ljava/util/List;Lnet/minecraft/server/permissions/PermissionSet;Ljava/util/List;)Lnet/minecraft/server/ReloadableServerResources;")) + private static ReloadableServerResources impl$onCreateResources( + final LayeredRegistryAccess fullLayers, + final HolderLookup.Provider loadingContext, + final FeatureFlagSet enabledFeatures, + final Commands.CommandSelection commandSelection, + final List postponedTags, + final PermissionSet functionCompilationPermissions, + final List newComponents, + final Operation original, + final @Local(argsOnly = true) ResourceManager resourceManager + ) { + final ReloadableServerResources instance = original.call(fullLayers, loadingContext, enabledFeatures, commandSelection, postponedTags, functionCompilationPermissions, newComponents); + if (instance.getCommands().getDispatcher() instanceof + final DelegatingCommandDispatcher delegatingCommandDispatcher) { + delegatingCommandDispatcher.permissionService(((ResourceManagerBridge) resourceManager).bridge$services().permissionService()); + } + return instance; + } +} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java index b06f9cb2cce..33f2f79f41d 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java @@ -24,8 +24,10 @@ */ package org.spongepowered.forge.mixin.core.server.level; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; @@ -37,10 +39,13 @@ @Mixin(ServerPlayer.class) public abstract class ServerPlayerMixin_Forge extends LivingEntityMixin_Forge { + @Shadow public abstract ServerLevel shadow$level(); + // override from LivingEntityMixin_Forge @Override protected void forge$onElytraUse(final CallbackInfo ci) { - final PhaseContext context = PhaseTracker.SERVER.getPhaseContext(); + final ServerLevel level = this.shadow$level(); + final PhaseContext context = PhaseTracker.getWorldInstance(level).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); final net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer) (Object) this; try (final EffectTransactor ignored = transactor.logPlayerInventoryChangeWithEffect(player, PlayerInventoryTransaction.EventCreator.STANDARD)) { diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Forge.java index d93af082be4..a808bfc08af 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Forge.java @@ -27,6 +27,8 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.network.protocol.game.ServerGamePacketListener; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.world.inventory.RecipeBookMenu; import net.minecraft.world.item.crafting.RecipeHolder; @@ -36,6 +38,7 @@ import org.spongepowered.api.item.inventory.crafting.CraftingInventory; import org.spongepowered.api.item.inventory.query.QueryTypes; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.accessor.world.inventory.AbstractFurnaceMenuAccessor; @@ -47,34 +50,38 @@ @Mixin(ServerGamePacketListenerImpl.class) public abstract class ServerGamePacketListenerImplMixin_Forge implements ServerGamePacketListener { - @SuppressWarnings({"unchecked", "rawtypes"}) - @WrapOperation(method = "lambda$handlePlaceRecipe$10", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/RecipeBookMenu;handlePlacement(ZLnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/server/level/ServerPlayer;)V")) - private void forge$onPlaceRecipe( - final RecipeBookMenu recipeBookMenu, final boolean shift, final RecipeHolder recipe, final net.minecraft.server.level.ServerPlayer player, - final Operation original - ) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + @Shadow public ServerPlayer player; + + @WrapOperation(method = "handlePlaceRecipe", + at = @At(value = "INVOKE", target = "net/minecraft/world/inventory/RecipeBookMenu.handlePlacement(ZZLnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/player/Inventory;)Lnet/minecraft/world/inventory/RecipeBookMenu$PostPlaceAction;")) + private RecipeBookMenu.PostPlaceAction forge$onPlaceRecipe( + final RecipeBookMenu recipeBookMenu, final boolean shift, final boolean isCreative, final RecipeHolder recipe, + final ServerLevel serverLevel, final net.minecraft.world.entity.player.Inventory inventory, + final Operation original) { + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); + final var player = this.player; + final RecipeBookMenu.PostPlaceAction result; final var menu = (Inventory) player.containerMenu; if (menu instanceof AbstractFurnaceMenuAccessor afm && afm.accessor$container() instanceof BlockEntity be) { try (final EffectTransactor ignored = transactor.logPlaceCookingRecipe(be, shift, recipe, player)) { - original.call(recipeBookMenu, shift, recipe, player); + result = original.call(recipeBookMenu, shift,isCreative, recipe, serverLevel, inventory); player.containerMenu.broadcastChanges(); - return; + return result; } } final Inventory craftInv = ((Inventory) player.containerMenu).query(QueryTypes.INVENTORY_TYPE.get().of(CraftingInventory.class)); if (!(craftInv instanceof CraftingInventory)) { - original.call(recipeBookMenu, shift, recipe, player); + result = original.call(recipeBookMenu, shift,isCreative, recipe, serverLevel, inventory); SpongeCommon.logger().warn("Detected crafting without a InventoryCrafting!? Crafting Event will not fire."); - return; + return result; } try (final EffectTransactor ignored = transactor.logPlaceRecipe(shift, recipe, player, (CraftingInventory) craftInv)) { - original.call(recipeBookMenu, shift, recipe, player); + result = original.call(recipeBookMenu, shift,isCreative, recipe, serverLevel, inventory); player.containerMenu.broadcastChanges(); } + return result; } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/tags/TagLoaderMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/tags/TagLoaderMixin_Forge.java index 14d240b5e56..ae4d761e740 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/tags/TagLoaderMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/tags/TagLoaderMixin_Forge.java @@ -27,7 +27,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.tags.TagEntry; import net.minecraft.tags.TagLoader; import org.spongepowered.asm.mixin.Mixin; @@ -45,7 +45,7 @@ @Mixin(TagLoader.class) public abstract class TagLoaderMixin_Forge implements TagLoaderBridge { - @WrapOperation(method = "build(Lnet/minecraft/tags/TagEntry$Lookup;Ljava/util/List;)Lcom/mojang/datafixers/util/Either;", + @WrapOperation(method = "tryBuildTag(Lnet/minecraft/tags/TagEntry$Lookup;Ljava/util/List;)Lcom/mojang/datafixers/util/Either;", at = @At(value = "INVOKE", target = "Lnet/minecraft/tags/TagEntry;build(Lnet/minecraft/tags/TagEntry$Lookup;Ljava/util/function/Consumer;)Z")) protected boolean forge$onBuildTag(final TagEntry instance, final TagEntry.Lookup lookup, final Consumer consumer, final Operation original, final TagEntry.Lookup $$0, final List $$1, final @Local TagLoader.EntryWithSource entry) { @@ -55,13 +55,13 @@ public abstract class TagLoaderMixin_Forge implements TagLoaderBridge { return this.bridge$acceptTag(instance, lookup, consumer, original::call, $$1, entry); } - @Inject(method = "lambda$build$6", at = @At("HEAD")) - private void forge$onStartBuildingTag(final TagEntry.Lookup $$0x, final Map> $$1x, final ResourceLocation $$2x, + @Inject(method = "lambda$build$1", at = @At("HEAD")) + private void forge$onStartBuildingTag(final TagEntry.Lookup $$0x, final Map> $$1x, final Identifier $$2x, final @Coerce Object $$3x, final CallbackInfo ci) { this.bridge$buildingTagKey($$2x); } - @Inject(method = "lambda$build$6", at = @At("RETURN")) + @Inject(method = "lambda$build$1", at = @At("RETURN")) private void forge$onDoneBuildingTag(final CallbackInfo ci) { this.bridge$buildingTagKey(null); } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java index 050c899e5cc..9e188e58814 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java @@ -24,23 +24,80 @@ */ package org.spongepowered.forge.mixin.core.world.entity; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.cause.entity.MovementTypes; +import org.spongepowered.api.event.entity.MoveEntityEvent; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.event.ShouldFire; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.math.vector.Vector3d; @Mixin(value = LivingEntity.class) public abstract class LivingEntityMixin_Forge { + @Unique + @Nullable + private Vector3d forge$preTeleportPosition; + @Inject( - method = "updateFallFlying", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/item/ItemStack;elytraFlightTick(Lnet/minecraft/world/entity/LivingEntity;I)Z", - shift = At.Shift.AFTER - ) + method = "updateFallFlying", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/item/ItemStack;hurtAndBreak(ILnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/entity/EquipmentSlot;)V", + shift = At.Shift.AFTER + ) ) protected void forge$onElytraUse(final CallbackInfo ci) { } + + @Inject(method = "randomTeleport", at = @At("HEAD")) + private void forge$snapshotPositionBeforeVanillaTeleportLogic(final double x, final double y, final double z, final boolean changeState, + final CallbackInfoReturnable cir) { + final Entity self = (Entity) (Object) this; + this.forge$preTeleportPosition = new Vector3d(self.getX(), self.getY(), self.getZ()); + } + + @Inject(method = "randomTeleport", at = @At(value = "RETURN", ordinal = 0, shift = At.Shift.BY, by = 2), cancellable = true) + private void forge$callMoveEntityEventForTeleport(final double x, final double y, final double z, final boolean changeState, + final CallbackInfoReturnable cir) { + if (!ShouldFire.MOVE_ENTITY_EVENT) { + return; + } + + final Entity self = (Entity) (Object) this; + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + frame.pushCause(this); + + // ENTITY_TELEPORT is our fallback context + if (!frame.currentContext().containsKey(EventContextKeys.MOVEMENT_TYPE)) { + frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.ENTITY_TELEPORT); + } + + final MoveEntityEvent event = SpongeEventFactory.createMoveEntityEvent(frame.currentCause(), + (org.spongepowered.api.entity.Entity) this, this.forge$preTeleportPosition, + new Vector3d(self.getX(), self.getY(), self.getZ()), + new Vector3d(x, y, z)); + + if (SpongeCommon.post(event)) { + self.teleportTo(this.forge$preTeleportPosition.x(), this.forge$preTeleportPosition.y(), + this.forge$preTeleportPosition.z()); + cir.setReturnValue(false); + return; + } + + self.teleportTo(event.destinationPosition().x(), event.destinationPosition().y(), + event.destinationPosition().z()); + } + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Shared_Attack_Impl.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge_Damage.java similarity index 52% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Shared_Attack_Impl.java rename to forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge_Damage.java index 323ee6c6143..35e92bf1cd8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Shared_Attack_Impl.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge_Damage.java @@ -22,31 +22,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity; +package org.spongepowered.forge.mixin.core.world.entity; import net.minecraft.world.entity.LivingEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.ModifyConstant; import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.common.util.DamageEventUtil; +import org.spongepowered.common.bridge.world.entity.TrackedDamageBridge; -// Forge and Vanilla -@Mixin(value = LivingEntity.class, priority = 900) -public class LivingEntityMixin_Shared_Attack_Impl { +@Mixin(LivingEntity.class) +public abstract class LivingEntityMixin_Forge_Damage implements TrackedDamageBridge { - protected DamageEventUtil.DamageEventResult attackImpl$actuallyHurtResult; + @ModifyConstant(method = "actuallyHurt", constant = @Constant(floatValue = 0), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraftforge/common/ForgeHooks;onLivingHurt(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/damagesource/DamageSource;F)F"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getDamageAfterArmorAbsorb(Lnet/minecraft/world/damagesource/DamageSource;F)F"))) + private float damage$doNotReturnEarly(final float constant) { + return Float.NaN; + } - /** - * Set absorbed damage after calling {@link LivingEntity#setAbsorptionAmount} in which we called the event - */ - @ModifyVariable(method = "actuallyHurt", ordinal = 2, - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;setAbsorptionAmount(F)V", ordinal = 0)), - at = @At(value = "STORE", ordinal = 0)) - public float attackImpl$setAbsorbed(final float value) { - if (this.attackImpl$actuallyHurtResult.event().isCancelled()) { - return 0; - } - return this.attackImpl$actuallyHurtResult.damageAbsorbed().orElse(0f); + @ModifyArg(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/common/ForgeHooks;onLivingDamage(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/damagesource/DamageSource;F)F")) + private float damage$firePostEvent_Living(final float damage) { + return this.damage$firePostEvent(damage); } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/item/ItemEntityMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/item/ItemEntityMixin_Forge.java index 1ad9b4ed978..516ee798c28 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/item/ItemEntityMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/item/ItemEntityMixin_Forge.java @@ -46,7 +46,7 @@ public abstract class ItemEntityMixin_Forge { final CallbackInfo ci ) { // Check the level is not null to avoid an NPE - if (level != null && !level.isClientSide) { + if (level != null && !level.isClientSide()) { this.lifespan = SpongeGameConfigs.getForWorld(level).get().entity.item.despawnRate; } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/player/PlayerMixin_Forge_Attack_Impl.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/player/PlayerMixin_Forge_Attack_Impl.java deleted file mode 100644 index be6dafce9d2..00000000000 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/player/PlayerMixin_Forge_Attack_Impl.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.mixin.core.world.entity.player; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.minecraftforge.event.entity.player.CriticalHitEvent; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.common.util.DamageEventUtil; -import org.spongepowered.forge.mixin.core.world.entity.LivingEntityMixin_Forge_Attack_Impl; - -@Mixin(Player.class) -public abstract class PlayerMixin_Forge_Attack_Impl extends LivingEntityMixin_Forge_Attack_Impl { - private DamageEventUtil.Attack attackImpl$attack; - - @WrapOperation(method = "attack", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/common/ForgeHooks;getCriticalHit(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/entity/Entity;ZF)Lnet/minecraftforge/event/entity/player/CriticalHitEvent;")) - private CriticalHitEvent attackImpl$critHook(final Player player, final Entity target, final boolean vanillaCritical, final float damageModifier, final Operation original) { - final CriticalHitEvent event = original.call(player, target, vanillaCritical, damageModifier); - if (event != null) { - this.attackImpl$attack.functions().add(DamageEventUtil.provideCriticalAttackFunction(this.attackImpl$attack.sourceEntity(), event.getDamageModifier())); - } - return event; - } - - @ModifyArg(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/common/ForgeHooks;onLivingDamage(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/damagesource/DamageSource;F)F")) - private float attackImpl$firePostEvent_Player(final float damage) { - if (this.attackImpl$actuallyHurtResult.event().isCancelled()) { - return 0; - } - return this.attackImpl$actuallyHurtFinalDamage; - } -} diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/player/PlayerMixin_Vanilla_Attack_Impl.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/player/PlayerMixin_Forge_Damage.java similarity index 57% rename from vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/player/PlayerMixin_Vanilla_Attack_Impl.java rename to forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/player/PlayerMixin_Forge_Damage.java index cf301126b75..efffd9fb1e6 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/player/PlayerMixin_Vanilla_Attack_Impl.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/player/PlayerMixin_Forge_Damage.java @@ -22,31 +22,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.mixin.core.world.entity.player; +package org.spongepowered.forge.mixin.core.world.entity.player; import net.minecraft.world.entity.player.Player; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.ModifyConstant; import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.common.util.DamageEventUtil; +import org.spongepowered.forge.mixin.core.world.entity.LivingEntityMixin_Forge_Damage; @Mixin(Player.class) -public abstract class PlayerMixin_Vanilla_Attack_Impl { - private DamageEventUtil.Attack attackImpl$attack; +public abstract class PlayerMixin_Forge_Damage extends LivingEntityMixin_Forge_Damage { - /** - * Captures the crit multiplier as a function - */ - @ModifyConstant(method = "attack", constant = @Constant(floatValue = 1.5F), - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;isSprinting()Z", ordinal = 1), - to = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/player/Player;walkDist:F")) - ) - public float attackImpl$critHook(final float constant) { - // if (isCritical) damage *= 1.5F; - final var bonusDamageFunc = DamageEventUtil.provideCriticalAttackFunction(this.attackImpl$attack.sourceEntity(), constant); - this.attackImpl$attack.functions().add(bonusDamageFunc); - return constant; + @ModifyConstant(method = "actuallyHurt", constant = @Constant(floatValue = 0), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraftforge/common/ForgeHooks;onLivingHurt(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/damagesource/DamageSource;F)F"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getDamageAfterArmorAbsorb(Lnet/minecraft/world/damagesource/DamageSource;F)F"))) + private float damage$doNotReturnEarly(final float constant) { + return Float.NaN; + } + + @ModifyArg(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/common/ForgeHooks;onLivingDamage(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/damagesource/DamageSource;F)F")) + private float damage$firePostEvent_Player(final float damage) { + return this.damage$firePostEvent(damage); } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/vehicle/BoatMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/vehicle/boat/BoatMixin_Forge.java similarity index 80% rename from forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/vehicle/BoatMixin_Forge.java rename to forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/vehicle/boat/BoatMixin_Forge.java index f00eb8a1c45..8af584df124 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/vehicle/BoatMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/vehicle/boat/BoatMixin_Forge.java @@ -22,25 +22,25 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.mixin.core.world.entity.vehicle; +package org.spongepowered.forge.mixin.core.world.entity.vehicle.boat; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.entity.vehicle.boat.AbstractBoat; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.bridge.world.entity.vehicle.BoatBridge; +import org.spongepowered.common.bridge.world.entity.vehicle.AbstractBoatBridge; -@Mixin(Boat.class) -public abstract class BoatMixin_Forge implements BoatBridge { +@Mixin(AbstractBoat.class) +public abstract class BoatMixin_Forge implements AbstractBoatBridge { @Redirect(method = "getGroundFriction", at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/level/block/state/BlockState;getFriction(Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/Entity;)F") + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/state/BlockState;getFriction(Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/Entity;)F") ) private float forge$getBlockFrictionIfBoatIsNotOverridingMovingOnLand(BlockState caller, LevelReader level, BlockPos pos, Entity entity) { final boolean movesOnLand = this.bridge$getMoveOnLand(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ChorusFruitItemMixin.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/item/TeleportRandomlyConsumeEffectMixin_Forge.java similarity index 74% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/item/ChorusFruitItemMixin.java rename to forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/item/TeleportRandomlyConsumeEffectMixin_Forge.java index 9ce6b8d0309..045cb37102e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ChorusFruitItemMixin.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/item/TeleportRandomlyConsumeEffectMixin_Forge.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.item; +package org.spongepowered.forge.mixin.core.world.item; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.item.ChorusFruitItem; +import net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.cause.entity.MovementTypes; @@ -34,16 +34,16 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.event.tracking.PhaseTracker; -@Mixin(ChorusFruitItem.class) -public abstract class ChorusFruitItemMixin { +@Mixin(TeleportRandomlyConsumeEffect.class) +public abstract class TeleportRandomlyConsumeEffectMixin_Forge { - @Redirect(method = "finishUsingItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;randomTeleport(DDDZ)Z")) - private boolean impl$createCauseFrameForTeleport(final LivingEntity entity, final double x, final double y, final double z, - final boolean p_213373_7_) { + @Redirect(method = "apply", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;randomTeleport(DDDZ)Z")) + private boolean forge$createCauseFrameForTeleport(final LivingEntity entity, final double x, final double y, final double z, + final boolean changeState) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.CHORUS_FRUIT.get()); - return entity.randomTeleport(x, y, z, p_213373_7_); + return entity.randomTeleport(x, y, z, changeState); } } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/ServerExplosionMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/ServerExplosionMixin_Forge.java new file mode 100644 index 00000000000..119f0f25cae --- /dev/null +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/ServerExplosionMixin_Forge.java @@ -0,0 +1,116 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.mixin.core.world.level; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.ServerExplosion; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.world.ExplosionEvent; +import org.spongepowered.api.world.explosion.Explosion; +import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.api.world.server.ServerWorld; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.event.ShouldFire; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.util.VecHelper; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Mixin(ServerExplosion.class) +public abstract class ServerExplosionMixin_Forge { + + // @formatter:off + @Shadow @Final private ServerLevel level; + + @Shadow protected abstract boolean shadow$interactsWithBlocks(); + // @formatter:on + + /** + * These two fields are used in the mixin below, but require a redirect specific for SpongeVanilla + * and SpongeForge as NeoForge makes a change to the method being used here. + * + * @see org.spongepowered.common.mixin.core.world.level.ServerExplosionMixin + */ + private boolean impl$shouldDamageEntities; + private double impl$knockbackMultiplier; + private List impl$affectedBlocks; + + @Redirect(method = "hurtEntities", at = @At(value = "INVOKE", + target = "Lnet/minecraft/server/level/ServerLevel;getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;")) + private List forge$throwEventToFilterEntitiesOnHurt(final ServerLevel instance, final Entity sourceEntity, final AABB aabb) { + // Forge hands this list to ForgeEventFactory.onExplosionDetonate where mods may mutate it, + // so every list returned here must be mutable. + final List entities; + if (this.impl$shouldDamageEntities) { + // filter out invulnerable entities before event + entities = instance.getEntities(sourceEntity, aabb).stream() + .filter(e -> !e.ignoreExplosion((net.minecraft.world.level.Explosion) this)) + .collect(Collectors.toCollection(ArrayList::new)); + } else { + entities = new ArrayList<>(); + } + + if (ShouldFire.EXPLOSION_EVENT_DETONATE) { + final var apiWorld = (ServerWorld) this.level; + final var apiEntities = entities.stream().map(org.spongepowered.api.entity.Entity.class::cast).collect(Collectors.toCollection(ArrayList::new)); + final var apiBlockPositions = this.impl$affectedBlocks.stream().map(bp -> ServerLocation.of(apiWorld, VecHelper.toVector3i(bp))).toList(); + final Cause cause = PhaseTracker.getInstance().currentCause(); + final ExplosionEvent.Detonate event = SpongeEventFactory.createExplosionEventDetonate(cause, apiBlockPositions, apiEntities, (Explosion) this, apiWorld); + if (SpongeCommon.post(event)) { + this.impl$affectedBlocks.clear(); // no blocks affected + return new ArrayList<>(); // no entities affected + } + if (this.shadow$interactsWithBlocks()) { + this.impl$affectedBlocks = event.affectedLocations().stream().map(VecHelper::toBlockPos).collect(Collectors.toList()); + } + if (this.impl$shouldDamageEntities) { + return event.entities().stream().map(Entity.class::cast).collect(Collectors.toCollection(ArrayList::new)); + } + } + return entities; + } + + + @WrapOperation(method = "hurtEntities", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/Vec3;scale(D)Lnet/minecraft/world/phys/Vec3;")) + private Vec3 forge$useKnockbackMultiplier(Vec3 instance, double $$0, Operation original) { + // Honor our knockback value from event + return original.call(instance, $$0 * this.impl$knockbackMultiplier); + } + +} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/FireBlockMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/FireBlockMixin_Forge.java index da13efce569..8b0d491eebc 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/FireBlockMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/FireBlockMixin_Forge.java @@ -49,7 +49,7 @@ public class FireBlockMixin_Forge { cancellable = true) private void impl$onCatchFirePreCheck( final Level world, final BlockPos pos, final int chance, final RandomSource random, final int age, final Direction dir, final CallbackInfo callbackInfo) { - if (!world.isClientSide) { + if (!world.isClientSide()) { if (ShouldFire.CHANGE_BLOCK_EVENT_PRE && SpongeCommonEventFactory.callChangeBlockEventPre((ServerLevelBridge) world, pos).isCancelled()) { callbackInfo.cancel(); } @@ -65,7 +65,7 @@ public class FireBlockMixin_Forge { cancellable = true) private void impl$onCatchFirePreCheckOther( final Level world, final BlockPos pos, final int chance, final RandomSource random, final int age, final Direction dir, final CallbackInfo callbackInfo) { - if (!world.isClientSide) { + if (!world.isClientSide()) { if (ShouldFire.CHANGE_BLOCK_EVENT_PRE && SpongeCommonEventFactory.callChangeBlockEventPre((ServerLevelBridge) world, pos).isCancelled()) { callbackInfo.cancel(); } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java index 8b0566a0a47..07a37b72c40 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java @@ -24,13 +24,16 @@ */ package org.spongepowered.forge.mixin.core.world.level.block.entity; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; -import net.minecraft.core.RegistryAccess; +import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.AbstractCookingRecipe; import net.minecraft.world.item.crafting.RecipeHolder; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; import net.minecraft.world.level.block.state.BlockState; import org.objectweb.asm.Opcodes; @@ -48,81 +51,119 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.block.entity.AbstractFurnaceBlockEntityBridge; import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.event.tracking.phase.general.GeneralPhase; +import org.spongepowered.common.event.tracking.phase.general.RecipeContext; import org.spongepowered.common.inventory.adapter.impl.slots.SlotAdapter; import org.spongepowered.common.item.util.ItemStackUtil; +import org.spongepowered.common.mixin.core.world.level.block.entity.BaseContainerBlockEntityMixin; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; -import javax.annotation.Nullable; - @Mixin(AbstractFurnaceBlockEntity.class) -public abstract class AbstractFurnaceBlockEntityMixin_Forge implements AbstractFurnaceBlockEntityBridge { +public abstract class AbstractFurnaceBlockEntityMixin_Forge extends BaseContainerBlockEntityMixin implements AbstractFurnaceBlockEntityBridge { // @formatter:off @Shadow protected NonNullList items; - @Shadow int cookingProgress; - @Shadow private boolean shadow$canBurn(RegistryAccess registryAccess, @Nullable RecipeHolder recipe, NonNullList slots, int maxStackSize) { - throw new UnsupportedOperationException("Shadowed canBurn"); - } + @Shadow private int cookingTimer; // @formatter:on + private static final ThreadLocal switched = new ThreadLocal<>(); private boolean forge$filledWaterBucket; // Tick up and Start - @Redirect(method = "serverTick", + @WrapOperation(method = "serverTick", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;canBurn(Lnet/minecraft/core/RegistryAccess;Lnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/core/NonNullList;I)Z", - ordinal = 1)) - private static boolean forge$checkIfCanSmelt(final AbstractFurnaceBlockEntity entityIn, - final RegistryAccess registryAccess, - final RecipeHolder recipe, - final NonNullList slots, - final int maxStackSize) { - final var entity = (AbstractFurnaceBlockEntityMixin_Forge) (Object) (entityIn); - if (!entity.shadow$canBurn(registryAccess, recipe, slots, maxStackSize)) { + target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;canBurn(Lnet/minecraft/core/NonNullList;ILnet/minecraft/world/item/ItemStack;)Z" + ) + ) + private static boolean forge$checkIfCanSmelt( + final AbstractFurnaceBlockEntity instance, + final NonNullList maxResultCount, + final int items, + final ItemStack maxStackSize, + final Operation original, + final ServerLevel level, + final BlockPos pos, + final BlockState state, + final AbstractFurnaceBlockEntity entityIn, + final @Local RecipeHolder recipe + ) { + if (!original.call(instance, maxResultCount, items, maxStackSize)) { return false; } - final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(slots.get(1)); + final var cookingRecipe = recipe.value(); + final var entity = (AbstractFurnaceBlockEntityMixin_Forge) (Object) (entityIn); + final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(maxResultCount.get(1)); final Cause cause = PhaseTracker.getInstance().currentCause(); - if (entity.cookingProgress == 0) { // Start + final var identifier = recipe.id().identifier(); + // Prepare a phase stack to capture the recipe in use + final var context = GeneralPhase.State.RECIPE_CRAFTING.createPhaseContext(PhaseTracker.getInstance()) + .recipe(recipe.value()) + .recipeID(identifier) + .furnace(entityIn) + .provideItem(maxResultCount.get(1)) + .buildAndSwitch(); + switched.set(context); + if (entity.cookingTimer == 0) { // Start final CookingEvent.Start event = SpongeEventFactory.createCookingEventStart(cause, (FurnaceBlockEntity) entityIn, Optional.of(fuel), - Optional.of((CookingRecipe) recipe.value()), Optional.of((ResourceKey) (Object) recipe.id())); + Optional.of((CookingRecipe) cookingRecipe), Optional.of((ResourceKey) (Object) identifier)); SpongeCommon.post(event); return !event.isCancelled(); } else { // Tick up final ItemStackSnapshot cooking = ItemStackUtil.snapshotOf(entity.items.get(0)); final CookingEvent.Tick event = SpongeEventFactory.createCookingEventTick(cause, (FurnaceBlockEntity) entityIn, cooking, Optional.of(fuel), - Optional.of((CookingRecipe) recipe.value()), Optional.of((ResourceKey) (Object) recipe.id())); + Optional.of((CookingRecipe) cookingRecipe), Optional.of((ResourceKey) (Object) identifier)); SpongeCommon.post(event); return !event.isCancelled(); } } + @Inject(method = "serverTick", at = @At("RETURN")) + private static void forge$closeRecipeContext( + final ServerLevel level, + final BlockPos pos, final BlockState state, + final AbstractFurnaceBlockEntity entity, + final CallbackInfo ci) { + if (switched.get() != null && PhaseTracker.getInstance().getPhaseContext() != switched.get()) { + SpongeCommon.logger().warn("Closed a recipe context that wasn't open!"); + } + final var closing = switched.get(); + switched.remove(); + if (closing != null) { + closing.close(); + } + } + // Tick down @Redirect(method = "serverTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;clamp(III)I")) - private static int forge$resetCookTimeIfCancelled(final int newCookTime, final int zero, final int totalCookTime, - final Level level, final BlockPos entityPos, final BlockState state, final AbstractFurnaceBlockEntity entityIn) { + private static int forge$resetCookTimeIfCancelled( + final int newCookTime, final int zero, final int totalCookTime, + final ServerLevel level, final BlockPos entityPos, final BlockState state, + final AbstractFurnaceBlockEntity entityIn) { final int clampedCookTime = Mth.clamp(newCookTime, zero, totalCookTime); final var entity = (AbstractFurnaceBlockEntityMixin_Forge) (Object) entityIn; - final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(entity.items.get(1)); + if (PhaseTracker.getInstance().getPhaseContext() != switched.get()) { + // we're in an invariant state + return clampedCookTime; + } + final var fuel = switched.get().usedItem(); final Cause cause = PhaseTracker.getInstance().currentCause(); - final var recipe = entity.bridge$getCurrentRecipe(); + final var recipe = switched.get().recipe(); final ItemStackSnapshot cooking = ItemStackUtil.snapshotOf(entity.items.get(0)); final CookingEvent.Tick event = SpongeEventFactory.createCookingEventTick(cause, (FurnaceBlockEntity) entityIn, cooking, Optional.of(fuel), - recipe.map(r -> (CookingRecipe) r.value()), recipe.map(r -> (ResourceKey) (Object) r.id())); + Optional.of((CookingRecipe) recipe), Optional.of((ResourceKey) (Object) switched.get().recipeID())); SpongeCommon.post(event); if (event.isCancelled()) { - return entity.cookingProgress; // dont tick down + return entity.cookingTimer; // dont tick down } return clampedCookTime; @@ -131,51 +172,74 @@ public abstract class AbstractFurnaceBlockEntityMixin_Forge implements AbstractF // Finish @Inject(method = "burn", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/NonNullList;set(ILjava/lang/Object;)Ljava/lang/Object;"), slice = @Slice( - from = @At(value = "FIELD", target = "Lnet/minecraft/world/level/block/Blocks;WET_SPONGE:Lnet/minecraft/world/level/block/Block;", opcode = Opcodes.GETSTATIC) + from = @At(value = "FIELD", target = "Lnet/minecraft/world/item/Items;WET_SPONGE:Lnet/minecraft/world/item/Item;", opcode = Opcodes.GETSTATIC) )) - private void forge$captureBucketFill(final RegistryAccess registryAccess, final RecipeHolder recipe, final NonNullList slots, final int maxStackSize, final CallbackInfoReturnable cir) { - this.forge$filledWaterBucket = true; + private void forge$captureBucketFill( + final NonNullList items, final ItemStack inputItemStack, final ItemStack result, final CallbackInfo ci + ) { + final Cause cause = PhaseTracker.getInstance().currentCause(); + final FurnaceBlockEntity entity = cause.first(FurnaceBlockEntity.class) + .orElseThrow(() -> new IllegalStateException("Expected to have a FurnaceBlockEntity in the Cause")); + ((AbstractFurnaceBlockEntityMixin_Forge) entity).forge$filledWaterBucket = true; } - @Inject( - method = "burn", - locals = LocalCapture.CAPTURE_FAILHARD, - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;shrink(I)V")) + @Inject(method = "burn", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/item/ItemStack;shrink(I)V", + shift = At.Shift.AFTER + )) private void forge$afterSmeltItem( - final RegistryAccess registryAccess, final RecipeHolder recipe, - final NonNullList slots, final int maxStackSize, - final CallbackInfoReturnable cir, - final ItemStack itemIn, final ItemStack recipeResult, final ItemStack itemOut + final NonNullList items, final ItemStack inputItemStack, final ItemStack result, final CallbackInfo ci ) { - final FurnaceBlockEntity entity = (FurnaceBlockEntity) this; + final ItemStack itemIn = items.get(0); + final ItemStack itemOut = items.get(2); + if (PhaseTracker.getInstance().getPhaseContext() != switched.get()) { + return; + } + final Cause cause = PhaseTracker.getInstance().currentCause(); + final FurnaceBlockEntity entity = (FurnaceBlockEntity) switched.get().furnace(); + final var recipe = switched.get().recipe(); + final var recipeID = switched.get().recipeID(); final List transactions = new ArrayList<>(); itemIn.grow(1); final ItemStackSnapshot originalSmeltItem = ItemStackUtil.snapshotOf(itemIn); itemIn.shrink(1); transactions.add(new SlotTransaction(entity.inventory().slot(0).get(), originalSmeltItem, ItemStackUtil.snapshotOf(itemIn))); - final boolean hasFuel = !this.forge$filledWaterBucket; - if (this.forge$filledWaterBucket) { - transactions.add(new SlotTransaction(entity.inventory().slot(1).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(slots.get(1)))); + final boolean hasFuel = !((AbstractFurnaceBlockEntityMixin_Forge) entity).forge$filledWaterBucket; + if (((AbstractFurnaceBlockEntityMixin_Forge) entity).forge$filledWaterBucket) { + transactions.add(new SlotTransaction(entity.inventory().slot(1).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(items.get(1)))); } - this.forge$filledWaterBucket = false; + ((AbstractFurnaceBlockEntityMixin_Forge) entity).forge$filledWaterBucket = false; if (itemOut.isEmpty()) { - transactions.add(new SlotTransaction(entity.inventory().slot(2).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(recipeResult))); - } else if (ItemStack.isSameItemSameComponents(itemOut, recipeResult)) { + transactions.add(new SlotTransaction(entity.inventory().slot(2).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(result))); + } else if (ItemStack.isSameItemSameComponents(itemOut, result)) { itemOut.shrink(1); final ItemStackSnapshot originalResult = ItemStackUtil.snapshotOf(itemOut); itemOut.grow(1); transactions.add(new SlotTransaction(entity.inventory().slot(2).get(), originalResult, ItemStackUtil.snapshotOf(itemOut))); } - final Optional fuel = hasFuel && !slots.get(1).isEmpty() ? Optional.of(ItemStackUtil.snapshotOf(slots.get(1))) : Optional.empty(); - final CookingEvent.Finish event = SpongeEventFactory.createCookingEventFinish(PhaseTracker.getInstance().currentCause(), entity, - fuel, Optional.of((CookingRecipe) recipe.value()), Optional.of((ResourceKey) (Object) recipe.id()), Collections.unmodifiableList(transactions)); + final Optional fuel = hasFuel && !items.get(1).isEmpty() ? Optional.of(ItemStackUtil.snapshotOf(items.get(1))) : Optional.empty(); + final CookingEvent.Finish event = SpongeEventFactory.createCookingEventFinish(cause, entity, + fuel, Optional.of((CookingRecipe) recipe), Optional.of((ResourceKey) (Object) recipeID), Collections.unmodifiableList(transactions)); SpongeCommon.post(event); for (final SlotTransaction transaction : transactions) { - transaction.custom().ifPresent(item -> slots.set(((SlotAdapter) transaction.slot()).getOrdinal(), ItemStackUtil.fromSnapshotToNative(item))); + transaction.custom().ifPresent(item -> items.set(((SlotAdapter) transaction.slot()).getOrdinal(), ItemStackUtil.fromSnapshotToNative(item))); } } + + // Interrupt-Active - e.g. a player removing the currently smelting item + @Inject( + method = "setItem", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;getTotalCookTime(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;)I" + ) + ) + private void forge$interruptSmelt(final CallbackInfo ci) { + this.impl$callInterruptSmeltEvent(); + } } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory_Forge.java deleted file mode 100644 index 95a4aa58821..00000000000 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory_Forge.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.forge.mixin.inventory.event.world.level.block.entity; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.Container; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.HopperBlockEntity; -import org.spongepowered.api.item.inventory.Inventory; -import org.spongepowered.api.item.inventory.transaction.SlotTransaction; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import org.spongepowered.common.accessor.world.level.block.entity.HopperBlockEntityAccessor; -import org.spongepowered.common.bridge.world.inventory.container.TrackedInventoryBridge; -import org.spongepowered.common.event.ShouldFire; -import org.spongepowered.common.event.inventory.InventoryEventFactory; -import org.spongepowered.common.inventory.util.InventoryUtil; - -@Mixin(HopperBlockEntity.class) -public abstract class HopperBlockEntityMixin_Inventory_Forge { - - @Redirect(method = "ejectItems", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;isFullContainer(Lnet/minecraft/world/Container;Lnet/minecraft/core/Direction;)Z")) - private static boolean forge$throwTransferPreIfNotFull( - final Container attachedContainer, final Direction direction, final Level level, final BlockPos pos, final HopperBlockEntity block - ) { - final boolean result = HopperBlockEntityAccessor.invoker$isFullContainer(attachedContainer, direction); - if (result || !ShouldFire.TRANSFER_INVENTORY_EVENT_PRE) { - return result; - } - return InventoryEventFactory.callTransferPre(InventoryUtil.toInventory(block), InventoryUtil.toInventory(attachedContainer)).isCancelled(); - } - -// Post Captured Transactions - - @Inject(method = "ejectItems", locals = LocalCapture.CAPTURE_FAILHARD, - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z", - ordinal = 1)) - private static void forge$afterPutStackInSlots( - final Level var0, final BlockPos var1, final HopperBlockEntity var3, - final CallbackInfoReturnable cir, final Container iInventory, final Direction enumFacing, - final int i, final ItemStack itemStack, final int count,final ItemStack itemStack1 - ) { - // after putStackInInventoryAllSlots if the transfer worked - if (ShouldFire.TRANSFER_INVENTORY_EVENT_POST && itemStack1.isEmpty()) { - // Capture Insert in Origin - final TrackedInventoryBridge capture = InventoryUtil.forCapture(var3); - final SlotTransaction sourceSlotTransaction = InventoryEventFactory.captureTransaction(capture, (Inventory) var3, i, itemStack); - // Call event - InventoryEventFactory.callTransferPost(capture, (Inventory) iInventory, InventoryUtil.toInventory(iInventory), itemStack, sourceSlotTransaction); - } - - InventoryUtil.updateInventoryNoEvents(var3); - InventoryUtil.updateInventoryNoEvents(iInventory); - } - -} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java index b3f8c60f891..0b0b2dd3e13 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java @@ -63,7 +63,7 @@ public abstract class ServerPlayerGameModeMixin_Forge_Tracker { final ItemStack itemStack, final Level level, final BlockState state, final BlockPos pos, final Player player ) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); // Log the prepare drops here because Forge rewrites // this method to call mine block before calling destroy block diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/BlockMixin_Forge_Tracker.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/BlockMixin_Forge_Tracker.java index 64005dd321a..efa5fa33f4b 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/BlockMixin_Forge_Tracker.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/BlockMixin_Forge_Tracker.java @@ -25,15 +25,18 @@ package org.spongepowered.forge.mixin.tracker.world.level.block; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootTable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.bridge.RegistryBackedTrackableBridge; import org.spongepowered.forge.mixin.tracker.world.level.block.state.BlockBehaviorMixin_Forge_Tracker; +import java.util.Optional; + @Mixin(Block.class) public abstract class BlockMixin_Forge_Tracker extends BlockBehaviorMixin_Forge_Tracker implements RegistryBackedTrackableBridge { @@ -42,7 +45,7 @@ public abstract class BlockMixin_Forge_Tracker extends BlockBehaviorMixin_Forge_ //@formatter:on @Override - protected void forgeTracker$initializeTrackingState(CallbackInfoReturnable cir) { + protected void forgeTracker$initializeTrackingState(CallbackInfoReturnable>> cir) { // TODO Not the best check but the tracker options only matter during block ticks... if (this.isRandomlyTicking(this.defaultBlockState())) { this.bridge$refreshTrackerStates(); diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Forge_Tracker.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Forge_Tracker.java index 8784263a379..a6d52a3eb16 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Forge_Tracker.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Forge_Tracker.java @@ -25,15 +25,18 @@ package org.spongepowered.forge.mixin.tracker.world.level.block.state; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootTable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.Optional; + @Mixin(BlockBehaviour.class) public abstract class BlockBehaviorMixin_Forge_Tracker { @Shadow protected abstract boolean isRandomlyTicking(BlockState $$0); @@ -42,10 +45,10 @@ public abstract class BlockBehaviorMixin_Forge_Tracker { method = "getLootTable", at = @At( value = "FIELD", - target = "Lnet/minecraft/world/level/block/state/BlockBehaviour;lootTableSupplier:Ljava/util/function/Supplier;", + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour;drops:Ljava/util/Optional;", remap = false ) ) - protected void forgeTracker$initializeTrackingState(CallbackInfoReturnable cir) { + protected void forgeTracker$initializeTrackingState(CallbackInfoReturnable>> cir) { } } diff --git a/forge/src/mixins/resources/mixins.spongeforge.core.json b/forge/src/mixins/resources/mixins.spongeforge.core.json index a84de19933e..f88f70fb92d 100644 --- a/forge/src/mixins/resources/mixins.spongeforge.core.json +++ b/forge/src/mixins/resources/mixins.spongeforge.core.json @@ -1,46 +1,45 @@ { - "required": true, - "parent": "mixins.sponge.parent.json", - "package": "org.spongepowered.forge.mixin.core", - "priority": 1301, - "mixins": [ - "api.event.EventMixin_Forge", - "api.event.block.ChangeBlockEvent_AllMixin_Forge", - "api.event.entity.ChangeEntityWorldEvent_PreMixin_Forge", - "api.event.entity.ChangeEventWorldEvent_PostMixin_Forge", - "commands.CommandsMixin_Forge", - "minecraftforge.MinecraftForgeMixin_Forge", - "minecraftforge.event.entity.EntityTravelToDimensionEventMixin_Forge", - "minecraftforge.event.entity.ItemTossEventMixin_Forge", - "minecraftforge.event.entity.player.PlayerEvent_PlayerChangedDimensionEventMixin_Forge", - "minecraftforge.event.world.BlockEvent_BreakEventMixin_Forge", - "minecraftforge.event.world.BlockEventMixin_Forge", - "minecraftforge.internal.BrandingControlMixin_Forge", - "minecraftforge.registries.ForgeRegistryMixin_Forge", - "minecraftforge.registries.GameDataMixin_Forge", - "minecraftforge.registries.RegistryManagerMixin_Forge", - "network.ConnectionMixin_Forge", - "network.protocol.common.ClientboundCustomPayloadPacketMixin_Forge", - "network.protocol.common.ServerboundCustomPayloadPacketMixin_Forge", - "server.BootstrapMixin_Forge", - "server.MinecraftServerMixin_Forge", - "server.commands.SpreadPlayersCommandMixin_Forge", - "server.level.ServerPlayerMixin_Forge", - "server.network.ServerGamePacketListenerImplMixin_Forge", - "tags.TagLoaderMixin_Forge", - "world.entity.LivingEntityMixin_Forge", - "world.entity.LivingEntityMixin_Forge_Attack_Impl", - "world.entity.item.ItemEntityMixin_Forge", - "world.entity.player.PlayerMixin_Forge_Attack_Impl", - "world.entity.vehicle.BoatMixin_Forge", - "world.level.block.FireBlockMixin_Forge", - "world.level.block.entity.AbstractFurnaceBlockEntityMixin_Forge" - ], - "client": [ - "client.MinecraftMixin_Forge", - "client.main.MainMixin_Forge" - ], - "server": [ - "server.MainMixin_Forge" - ] + "required": true, + "parent": "mixins.sponge.parent.json", + "package": "org.spongepowered.forge.mixin.core", + "priority": 1301, + "injectors": { + "maxShiftBy": 3 + }, + "mixins": [ + "commands.CommandsMixin_Forge", + "minecraftforge.ForgeHooksMixin_Forge", + "minecraftforge.MinecraftForgeMixin_Forge", + "minecraftforge.internal.BrandingControlMixin_Forge", + "minecraftforge.registries.ForgeRegistryMixin_Forge", + "minecraftforge.registries.GameDataMixin_Forge", + "minecraftforge.registries.NamespacedWrapperMixin_Forge", + "minecraftforge.registries.RegistryManagerMixin_Forge", + "network.ConnectionMixin_Forge", + "network.protocol.common.ClientboundCustomPayloadPacketMixin_Forge", + "network.protocol.common.ServerboundCustomPayloadPacketMixin_Forge", + "resources.RegistryDataLoaderMixin_Forge", + "server.BootstrapMixin_Forge", + "server.MinecraftServerMixin_Forge", + "server.ReloadableServerResourcesMixin_Forge", + "server.level.ServerPlayerMixin_Forge", + "server.network.ServerGamePacketListenerImplMixin_Forge", + "tags.TagLoaderMixin_Forge", + "world.entity.LivingEntityMixin_Forge", + "world.entity.LivingEntityMixin_Forge_Damage", + "world.entity.item.ItemEntityMixin_Forge", + "world.entity.player.PlayerMixin_Forge_Damage", + "world.entity.vehicle.boat.BoatMixin_Forge", + "world.item.TeleportRandomlyConsumeEffectMixin_Forge", + "world.level.ServerExplosionMixin_Forge", + "world.level.block.FireBlockMixin_Forge", + "world.level.block.entity.AbstractFurnaceBlockEntityMixin_Forge" + ], + "client": [ + "client.MinecraftMixin_Forge", + "client.main.MainMixin_Forge" + ], + "server": [ + "server.MainMixin_Forge" + ] } \ No newline at end of file diff --git a/forge/src/mixins/resources/mixins.spongeforge.core.shared.json b/forge/src/mixins/resources/mixins.spongeforge.core.shared.json index b322779182e..00b0a290898 100644 --- a/forge/src/mixins/resources/mixins.spongeforge.core.shared.json +++ b/forge/src/mixins/resources/mixins.spongeforge.core.shared.json @@ -4,10 +4,10 @@ "package": "org.spongepowered.common.mixin.core", "priority": 1301, "mixins": [ - "server.level.ServerEntityMixin_Shared", - "world.entity.LivingEntityMixin_Shared_Attack_Impl", - "world.entity.player.PlayerMixin_Shared_Attack_Impl", - "world.entity.projectile.FishingHookMixin_Shared" + "world.entity.LivingEntityMixin_Shared_Damage", + "world.entity.player.PlayerMixin_Shared_Damage", + "world.entity.projectile.FishingHookMixin_Shared", + "world.item.crafting.RecipeManagerMixin_Shared" ], "overwrites": { "conformVisibility": true diff --git a/forge/src/mixins/resources/mixins.spongeforge.inventory.json b/forge/src/mixins/resources/mixins.spongeforge.inventory.json index 21d0444a46e..890e2ee51d5 100644 --- a/forge/src/mixins/resources/mixins.spongeforge.inventory.json +++ b/forge/src/mixins/resources/mixins.spongeforge.inventory.json @@ -5,6 +5,5 @@ "target": "@env(DEFAULT)", "priority": 1300, "mixins": [ - "event.world.level.block.entity.HopperBlockEntityMixin_Inventory_Forge" ] } diff --git a/generator/build.gradle.kts b/generator/build.gradle.kts index da7ca6bedc2..47243f5c0d4 100644 --- a/generator/build.gradle.kts +++ b/generator/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { } val apiBase = rootProject.file("SpongeAPI/src/main/java/") +val implBase = rootProject.file("src/main/java/") val temporaryLicenseHeader = project.layout.buildDirectory.file("api-gen-license-header.txt") tasks.register("generateApiData", JavaExec::class) { group = "sponge" @@ -30,7 +31,7 @@ tasks.register("generateApiData", JavaExec::class) { classpath(sourceSets.main.map { it.output }, sourceSets.main.map { it.runtimeClasspath }) mainClass.set("org.spongepowered.vanilla.generator.GeneratorMain") - args(apiBase.canonicalPath, temporaryLicenseHeader.get().asFile.canonicalPath) + args(apiBase.canonicalPath, implBase.canonicalPath, temporaryLicenseHeader.get().asFile.canonicalPath) doFirst { // Write a template-expanded license header to the temporary file diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStateDataProviderGenerator.java b/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStateDataProviderGenerator.java index a425ff28353..7c3de34d2d0 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStateDataProviderGenerator.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStateDataProviderGenerator.java @@ -24,11 +24,14 @@ */ package org.spongepowered.vanilla.generator; -import com.squareup.javapoet.CodeBlock; -import com.squareup.javapoet.MethodSpec; +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.stmt.BlockStmt; +import com.github.javaparser.ast.stmt.ExpressionStmt; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.Property; +import org.tinylog.Logger; import java.io.IOException; import java.lang.reflect.Field; @@ -37,10 +40,9 @@ import java.util.TreeSet; import java.util.stream.Collectors; -import javax.lang.model.element.Modifier; - /** - * Generates the registration of catalog classes for {@link BlockState} properties. + * Updates the register method of the {@code BlockStateDataProvider} in the Sponge implementation + * source set with the current set of {@link BlockState} properties. */ public class BlockStateDataProviderGenerator implements Generator { @@ -51,25 +53,44 @@ public String name() { @Override public void generate(Context ctx) throws IOException { - final var clazz = Types.utilityClass( - "BlockStateDataProvider", - "" - ); + final var cu = ctx.implCompilationUnit("data.provider.block.state", "BlockStateDataProvider"); + final var primaryType = cu.getPrimaryType() + .orElseThrow(() -> new IllegalStateException("Could not find primary type in BlockStateDataProvider")); + + final var registerMethod = primaryType.getMembers().stream() + .filter(m -> m instanceof MethodDeclaration) + .map(m -> (MethodDeclaration) m) + .filter(m -> m.getNameAsString().equals("register")) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Could not find register method in BlockStateDataProvider")); + + final var body = registerMethod.getBody() + .orElseThrow(() -> new IllegalStateException("register method has no body")); - final var codeBlock = CodeBlock.builder(); + // Collect all non-registerProperty statements (e.g. the trailing registrator.asImmutable block) + final var trailingStatements = body.getStatements().stream() + .filter(stmt -> { + if (stmt instanceof ExpressionStmt es) { + return !es.toString().contains("registerProperty"); + } + return true; + }) + .toList(); + + // Rebuild the body: registerProperty calls first, then trailing statements + final var newBody = new BlockStmt(); for (final String property : this.vanillaProperties()) { - codeBlock.addStatement("BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.$L, BlockStateProperties.$L)", property, property); + newBody.addStatement(StaticJavaParser.parseStatement( + "BlockStateDataProvider.registerProperty(registrator, BlockStateKeys." + property + ", BlockStateProperties." + property + ");" + )); + } + for (final var stmt : trailingStatements) { + newBody.addStatement(stmt.clone()); } - clazz.addMethod(MethodSpec.methodBuilder("register") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .addCode(codeBlock.build()) - .build() - ); - - ctx.write("data", clazz.build()); + registerMethod.setBody(newBody); - final var cu = ctx.compilationUnit("data", "BlockStateDataProvider"); + Logger.info("Updated BlockStateDataProvider.register() with {} property registrations", this.vanillaProperties().size()); } private Set vanillaProperties() { diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStatePropertiesGenerator.java b/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStatePropertiesGenerator.java index 202e77ae676..a0f6566e833 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStatePropertiesGenerator.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStatePropertiesGenerator.java @@ -32,6 +32,8 @@ import net.minecraft.core.FrontAndTop; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.CopperGolemStatueBlock; +import net.minecraft.world.level.block.WeatheringCopper; import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerState; import net.minecraft.world.level.block.entity.vault.VaultState; import net.minecraft.world.level.block.state.BlockState; @@ -43,9 +45,9 @@ import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.ChestType; import net.minecraft.world.level.block.state.properties.ComparatorMode; +import net.minecraft.world.level.block.state.properties.CreakingHeartState; import net.minecraft.world.level.block.state.properties.DoorHingeSide; import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; -import net.minecraft.world.level.block.state.properties.DripstoneThickness; import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.level.block.state.properties.Half; import net.minecraft.world.level.block.state.properties.IntegerProperty; @@ -55,9 +57,12 @@ import net.minecraft.world.level.block.state.properties.RailShape; import net.minecraft.world.level.block.state.properties.RedstoneSide; import net.minecraft.world.level.block.state.properties.SculkSensorPhase; +import net.minecraft.world.level.block.state.properties.SideChainPart; import net.minecraft.world.level.block.state.properties.SlabType; +import net.minecraft.world.level.block.state.properties.SpeleothemThickness; import net.minecraft.world.level.block.state.properties.StairsShape; import net.minecraft.world.level.block.state.properties.StructureMode; +import net.minecraft.world.level.block.state.properties.TestBlockMode; import net.minecraft.world.level.block.state.properties.Tilt; import net.minecraft.world.level.block.state.properties.WallSide; import org.tinylog.Logger; @@ -143,11 +148,17 @@ static PropertyType ofProperty(final Property prop) { vanillaEnumTypeMapping.put(DoorHingeSide.class, BlockStatePropertiesGenerator.inDataTypePkg("DoorHinge")); vanillaEnumTypeMapping.put(NoteBlockInstrument.class, BlockStatePropertiesGenerator.inDataTypePkg("InstrumentType")); vanillaEnumTypeMapping.put(StairsShape.class, BlockStatePropertiesGenerator.inDataTypePkg("StairShape")); - vanillaEnumTypeMapping.put(DripstoneThickness.class, BlockStatePropertiesGenerator.inDataTypePkg("DripstoneSegment")); + vanillaEnumTypeMapping.put(SpeleothemThickness.class, BlockStatePropertiesGenerator.inDataTypePkg("DripstoneSegment")); vanillaEnumTypeMapping.put(FrontAndTop.class, BlockStatePropertiesGenerator.inDataTypePkg("JigsawBlockOrientation")); vanillaEnumTypeMapping.put(ComparatorMode.class, BlockStatePropertiesGenerator.inDataTypePkg("ComparatorMode")); vanillaEnumTypeMapping.put(TrialSpawnerState.class, BlockStatePropertiesGenerator.inDataTypePkg("TrialSpawnerState")); vanillaEnumTypeMapping.put(VaultState.class, BlockStatePropertiesGenerator.inDataTypePkg("VaultState")); + vanillaEnumTypeMapping.put(CreakingHeartState.class, BlockStatePropertiesGenerator.inDataTypePkg("CreakingHeartState")); + vanillaEnumTypeMapping.put(TestBlockMode.class, BlockStatePropertiesGenerator.inDataTypePkg("TestBlockMode")); + vanillaEnumTypeMapping.put(SideChainPart.class, BlockStatePropertiesGenerator.inDataTypePkg("SideChain")); + vanillaEnumTypeMapping.put(CopperGolemStatueBlock.Pose.class, BlockStatePropertiesGenerator.inDataTypePkg("CopperGolemPose")); + vanillaEnumTypeMapping.put(WeatheringCopper.WeatherState.class, BlockStatePropertiesGenerator.inDataTypePkg("CopperOxidation")); + // Custom Mapping required see StateHolderMixin_API final ClassName portionTypeClass = BlockStatePropertiesGenerator.inDataTypePkg("PortionType"); diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStatePropertyKeysGenerator.java b/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStatePropertyKeysGenerator.java index d00816a08ce..66099fbcd7a 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStatePropertyKeysGenerator.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/BlockStatePropertyKeysGenerator.java @@ -33,6 +33,8 @@ import com.squareup.javapoet.TypeVariableName; import net.minecraft.core.Direction; import net.minecraft.core.FrontAndTop; +import net.minecraft.world.level.block.CopperGolemStatueBlock; +import net.minecraft.world.level.block.WeatheringCopper; import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerState; import net.minecraft.world.level.block.entity.vault.VaultState; import net.minecraft.world.level.block.state.BlockState; @@ -44,21 +46,25 @@ import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.ChestType; import net.minecraft.world.level.block.state.properties.ComparatorMode; +import net.minecraft.world.level.block.state.properties.CreakingHeartState; import net.minecraft.world.level.block.state.properties.DoorHingeSide; import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; -import net.minecraft.world.level.block.state.properties.DripstoneThickness; import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.level.block.state.properties.Half; import net.minecraft.world.level.block.state.properties.IntegerProperty; import net.minecraft.world.level.block.state.properties.NoteBlockInstrument; import net.minecraft.world.level.block.state.properties.PistonType; +import net.minecraft.world.level.block.state.properties.PotentSulfurState; import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.block.state.properties.RailShape; import net.minecraft.world.level.block.state.properties.RedstoneSide; import net.minecraft.world.level.block.state.properties.SculkSensorPhase; +import net.minecraft.world.level.block.state.properties.SideChainPart; import net.minecraft.world.level.block.state.properties.SlabType; +import net.minecraft.world.level.block.state.properties.SpeleothemThickness; import net.minecraft.world.level.block.state.properties.StairsShape; import net.minecraft.world.level.block.state.properties.StructureMode; +import net.minecraft.world.level.block.state.properties.TestBlockMode; import net.minecraft.world.level.block.state.properties.Tilt; import net.minecraft.world.level.block.state.properties.WallSide; import org.tinylog.Logger; @@ -128,6 +134,7 @@ static BlockStatePropertyKeysGenerator.PropertyType ofProperty(final Property static { vanillaEnumTypeMapping.put(StructureMode.class, BlockStatePropertyKeysGenerator.inDataTypePkg("StructureMode")); vanillaEnumTypeMapping.put(PistonType.class, BlockStatePropertyKeysGenerator.inDataTypePkg("PistonType")); + vanillaEnumTypeMapping.put(PotentSulfurState.class, BlockStatePropertyKeysGenerator.inDataTypePkg("PotentSulfurState")); vanillaEnumTypeMapping.put(BambooLeaves.class, BlockStatePropertyKeysGenerator.inDataTypePkg("BambooLeavesType")); vanillaEnumTypeMapping.put(WallSide.class, BlockStatePropertyKeysGenerator.inDataTypePkg("WallConnectionState")); vanillaEnumTypeMapping.put(RailShape.class, BlockStatePropertyKeysGenerator.inDataTypePkg("RailDirection")); @@ -141,11 +148,16 @@ static BlockStatePropertyKeysGenerator.PropertyType ofProperty(final Property vanillaEnumTypeMapping.put(DoorHingeSide.class, BlockStatePropertyKeysGenerator.inDataTypePkg("DoorHinge")); vanillaEnumTypeMapping.put(NoteBlockInstrument.class, BlockStatePropertyKeysGenerator.inDataTypePkg("InstrumentType")); vanillaEnumTypeMapping.put(StairsShape.class, BlockStatePropertyKeysGenerator.inDataTypePkg("StairShape")); - vanillaEnumTypeMapping.put(DripstoneThickness.class, BlockStatePropertyKeysGenerator.inDataTypePkg("DripstoneSegment")); + vanillaEnumTypeMapping.put(SpeleothemThickness.class, BlockStatePropertyKeysGenerator.inDataTypePkg("DripstoneSegment")); vanillaEnumTypeMapping.put(FrontAndTop.class, BlockStatePropertyKeysGenerator.inDataTypePkg("JigsawBlockOrientation")); vanillaEnumTypeMapping.put(ComparatorMode.class, BlockStatePropertyKeysGenerator.inDataTypePkg("ComparatorMode")); vanillaEnumTypeMapping.put(TrialSpawnerState.class, BlockStatePropertyKeysGenerator.inDataTypePkg("TrialSpawnerState")); vanillaEnumTypeMapping.put(VaultState.class, BlockStatePropertyKeysGenerator.inDataTypePkg("VaultState")); + vanillaEnumTypeMapping.put(CreakingHeartState.class, BlockStatePropertyKeysGenerator.inDataTypePkg("CreakingHeartState")); + vanillaEnumTypeMapping.put(TestBlockMode.class, BlockStatePropertyKeysGenerator.inDataTypePkg("TestBlockMode")); + vanillaEnumTypeMapping.put(SideChainPart.class, BlockStatePropertyKeysGenerator.inDataTypePkg("SideChain")); + vanillaEnumTypeMapping.put(CopperGolemStatueBlock.Pose.class, BlockStatePropertyKeysGenerator.inDataTypePkg("CopperGolemPose")); + vanillaEnumTypeMapping.put(WeatheringCopper.WeatherState.class, BlockStatePropertyKeysGenerator.inDataTypePkg("CopperOxidation")); // Custom Mapping required see StateHolderMixin_API final ClassName portionTypeClass = BlockStatePropertyKeysGenerator.inDataTypePkg("PortionType"); diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/ClassFieldsValidator.java b/generator/src/main/java/org/spongepowered/vanilla/generator/ClassFieldsValidator.java new file mode 100644 index 00000000000..afa6de3b4fc --- /dev/null +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/ClassFieldsValidator.java @@ -0,0 +1,204 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.generator; + +import static com.github.javaparser.ast.Modifier.createModifierList; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.Modifier; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.FieldDeclaration; +import com.github.javaparser.ast.body.VariableDeclarator; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.expr.NameExpr; +import com.github.javaparser.ast.expr.StringLiteralExpr; +import com.github.javaparser.javadoc.Javadoc; +import com.github.javaparser.javadoc.description.JavadocDescription; +import net.minecraft.resources.Identifier; +import org.tinylog.Logger; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; + +public class ClassFieldsValidator implements Generator { + + private final String relativePackageName; + private final String targetClassSimpleName; + private final Class clazz; + private final Function mapping; + + public ClassFieldsValidator( + final String targetRelativePackage, + final String targetClassSimpleName, + final Class clazz + ) { + this(targetRelativePackage, targetClassSimpleName, clazz, name -> Identifier.tryBuild("sponge", name.toLowerCase(Locale.ROOT))); + } + + public ClassFieldsValidator( + final String targetRelativePackage, + final String targetClassSimpleName, + final Class clazz, + final Function mapping + ) { + this.relativePackageName = targetRelativePackage; + this.targetClassSimpleName = targetClassSimpleName; + this.clazz = clazz; + this.mapping = mapping; + } + + @Override + public String name() { + return "elements of class " + this.clazz.getName(); + } + + @Override + @SuppressWarnings("unchecked") + public void generate(final Context ctx) { + // We read an existing class, then go through existing fields, make sure they exist, and add any new ones. + final var compilationUnit = ctx.compilationUnit(this.relativePackageName, this.targetClassSimpleName); + final var primaryTypeDeclaration = compilationUnit.getPrimaryType() + .orElseThrow(() -> new IllegalStateException("Could not find primary type for registry type " + this.targetClassSimpleName)); + + primaryTypeDeclaration.setJavadocComment(new Javadoc(JavadocDescription.parseText(Generator.GENERATED_FILE_JAVADOCS))); + + final List map = new ArrayList<>(); + for (final var field : this.clazz.getDeclaredFields()) { + if (!java.lang.reflect.Modifier.isPublic(field.getModifiers()) + || !java.lang.reflect.Modifier.isStatic(field.getModifiers())) { + continue; + + } + final var name = field.getName(); + final var key = this.mapping.apply(name); + map.add(key); + } + + // Find index of first field member + // Take out all field members from the members list + final var members = primaryTypeDeclaration.getMembers(); + final var fields = new HashMap(); + int lastNonFieldIndex = -1; + for (final var it = members.listIterator(); it.hasNext(); ) { + final var node = it.next(); + if (lastNonFieldIndex == -1 && node instanceof FieldDeclaration) { + lastNonFieldIndex = it.nextIndex() - 1; + } + + if (node instanceof FieldDeclaration) { + final var field = (FieldDeclaration) node; + fields.put(this.extractFieldIdentifier(field), field); + it.remove(); + } + } + + // Now, iterate the registry, discovering which fields were added and removed + final var added = new HashSet(); + final var processedFields = new ArrayList(map.size()); + final Set allKeys = new HashSet<>(map); + for (final Identifier key : allKeys) { + final FieldDeclaration existing = fields.remove(key); + if (existing != null) { + processedFields.add(existing); + } else { + added.add(key); + processedFields.add(this.makeField(this.targetClassSimpleName, "key", key)); + } + } + + // Sort field entries and add them back to the class + processedFields.sort(Comparator.comparing(field -> field.getVariable(0).getNameAsString())); + primaryTypeDeclaration.getMembers().addAll(lastNonFieldIndex, processedFields); + + if (!added.isEmpty()) { + Logger.info("Added {} entries to {} that will require manual action to implement: {}", added.size(), this.targetClassSimpleName, added); + } + if (!fields.isEmpty()) { + Logger.info("Removed {} entries from {} because they are no longer present in the game: {}", fields.size(), this.targetClassSimpleName, fields.keySet()); + } + } + + // Attempt to get a resource location from the field by parsing its initializer + private Identifier extractFieldIdentifier(final FieldDeclaration declaration) { + if (declaration.getVariables().isEmpty()) { + throw new IllegalStateException("No variables for " + declaration); + } + final VariableDeclarator var = declaration.getVariable(0); + final Expression initializer = var.getInitializer().orElse(null); + if (!(initializer instanceof MethodCallExpr) || ((MethodCallExpr) initializer).getArguments().size() != 1) { + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + } + + final Expression argument = ((MethodCallExpr) initializer).getArgument(0); + if (!(argument instanceof final MethodCallExpr keyInitializer) + || keyInitializer.getArguments().size() < 1) { + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + } + + if (keyInitializer.getArguments().size() == 1) { // method name as namespace + return Identifier.fromNamespaceAndPath(keyInitializer.getNameAsString(), keyInitializer.getArgument(0).asStringLiteralExpr().asString()); + } else if (keyInitializer.getArguments().size() == 2) { // (namespace, path) + return Identifier.fromNamespaceAndPath( + keyInitializer.getArgument(0).asStringLiteralExpr().asString(), + keyInitializer.getArgument(1).asStringLiteralExpr().asString() + ); + } else { + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + } + + } + + private FieldDeclaration makeField(final String ownType, final String factoryMethod, final Identifier element) { + final FieldDeclaration fieldDeclaration = new FieldDeclaration(); + final VariableDeclarator variable = new VariableDeclarator(StaticJavaParser.parseType("DefaultedRegistryReference"), Types.keyToFieldName(element.getPath())); + fieldDeclaration.getVariables().add(variable); + fieldDeclaration.setModifiers(createModifierList(Modifier.Keyword.PUBLIC, Modifier.Keyword.STATIC, Modifier.Keyword.FINAL)); + variable.setInitializer(new MethodCallExpr(new NameExpr(ownType), factoryMethod, new NodeList<>(RegistryEntriesValidator.resourceKey(element)))); + return fieldDeclaration; + } + + public static MethodCallExpr resourceKey(final Identifier location) { + Objects.requireNonNull(location, "location"); + final var resourceKey = new NameExpr("ResourceKey"); + return switch (location.getNamespace()) { + case "minecraft" -> + new MethodCallExpr(resourceKey, "minecraft", new NodeList<>(new StringLiteralExpr(location.getPath()))); + case "brigadier" -> + new MethodCallExpr(resourceKey, "brigadier", new NodeList<>(new StringLiteralExpr(location.getPath()))); + case "sponge" -> + new MethodCallExpr(resourceKey, "sponge", new NodeList<>(new StringLiteralExpr(location.getPath()))); + default -> new MethodCallExpr( + resourceKey, "of", new NodeList<>(new StringLiteralExpr(location.getNamespace()), new StringLiteralExpr(location.getPath()))); + }; + } +} diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/Context.java b/generator/src/main/java/org/spongepowered/vanilla/generator/Context.java index a31b37a5a04..0ad96749f19 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/Context.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/Context.java @@ -47,27 +47,34 @@ public final class Context { static final String INDENT = " "; static final String BASE_PACKAGE = "org.spongepowered.api"; + static final String IMPL_BASE_PACKAGE = "org.spongepowered.common"; private final Path outputDirectory; + private final Path implOutputDirectory; private final RegistryAccess registries; private final ReloadableServerResources resources; private final String licenseHeader; private final SourceRoot sourceRoot; + private final SourceRoot implSourceRoot; - Context(final Path outputDirectory, final RegistryAccess registries, final ReloadableServerResources resources, final String licenseHeader) { + Context(final Path outputDirectory, final Path implOutputDirectory, final RegistryAccess registries, final ReloadableServerResources resources, final String licenseHeader) { this.outputDirectory = outputDirectory; + this.implOutputDirectory = implOutputDirectory; this.registries = registries; this.resources = resources; this.licenseHeader = licenseHeader; - this.sourceRoot = new SourceRoot(outputDirectory); - this.sourceRoot.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_16); final var config = new DefaultPrinterConfiguration(); config.addOption(new DefaultConfigurationOption(DefaultPrinterConfiguration.ConfigOption.PRINT_COMMENTS, true)); final var printer = new DefaultPrettyPrinter(config); + this.sourceRoot = new SourceRoot(outputDirectory); + this.sourceRoot.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_16); this.sourceRoot.setPrinter(printer::print); + this.implSourceRoot = new SourceRoot(implOutputDirectory); + this.implSourceRoot.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_16); + this.implSourceRoot.setPrinter(printer::print); } public String gameVersion() { - return SharedConstants.getCurrentVersion().getName(); + return SharedConstants.getCurrentVersion().name(); } public RegistryAccess registries() { @@ -85,9 +92,24 @@ public ClassName relativeClass(final String relativePackage, final String simple public CompilationUnit compilationUnit(final String relativePackage, final String simpleName) { final String pkg = relativePackage.isBlank() ? Context.BASE_PACKAGE : String.join(".", Context.BASE_PACKAGE, relativePackage); - final CompilationUnit unit = this.sourceRoot.parse(pkg, simpleName + ".java"); - LexicalPreservingPrinter.setup(unit); - return unit; + try { + final CompilationUnit unit = this.sourceRoot.parse(pkg, simpleName + ".java"); + LexicalPreservingPrinter.setup(unit); + return unit; + } catch (Exception e) { + throw new RuntimeException("Failed to parse " + simpleName + ".java", e); + } + } + + public CompilationUnit implCompilationUnit(final String relativePackage, final String simpleName) { + final String pkg = relativePackage.isBlank() ? Context.IMPL_BASE_PACKAGE : String.join(".", Context.IMPL_BASE_PACKAGE, relativePackage); + try { + final CompilationUnit unit = this.implSourceRoot.parse(pkg, simpleName + ".java"); + LexicalPreservingPrinter.setup(unit); + return unit; + } catch (Exception e) { + throw new RuntimeException("Failed to parse " + simpleName + ".java", e); + } } /** @@ -136,5 +158,6 @@ public void write(final String relativePackage, final TypeSpec spec) throws IOEx */ void complete() { this.sourceRoot.saveAll(StandardCharsets.UTF_8); + this.implSourceRoot.saveAll(StandardCharsets.UTF_8); } } diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/EnumEntriesValidator.java b/generator/src/main/java/org/spongepowered/vanilla/generator/EnumEntriesValidator.java index d8a16873899..fe97aea0807 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/EnumEntriesValidator.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/EnumEntriesValidator.java @@ -37,7 +37,7 @@ import com.github.javaparser.ast.expr.StringLiteralExpr; import com.github.javaparser.javadoc.Javadoc; import com.github.javaparser.javadoc.description.JavadocDescription; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.util.StringRepresentable; import org.tinylog.Logger; @@ -91,7 +91,7 @@ public void generate(final Context ctx) { // Find index of first field member // Take out all field members from the members list final var members = primaryTypeDeclaration.getMembers(); - final var fields = new HashMap(); + final var fields = new HashMap(); int lastNonFieldIndex = -1; for (final var it = members.listIterator(); it.hasNext();) { final var node = it.next(); @@ -107,7 +107,7 @@ public void generate(final Context ctx) { } // Now, iterate the registry, discovering which fields were added and removed - final var added = new HashSet(); + final var added = new HashSet(); final var processedFields = new ArrayList(map.length); for (final Enum f : map) { final String name; @@ -123,7 +123,7 @@ public void generate(final Context ctx) { } catch (Exception e) { throw new IllegalStateException("Failed to name for enum field in class " + this.clazz.getName(), e); } - final ResourceLocation key = ResourceLocation.fromNamespaceAndPath(this.namespace, name); + final Identifier key = Identifier.fromNamespaceAndPath(this.namespace, name); final FieldDeclaration existing = fields.remove(key); if (existing != null) { @@ -136,7 +136,11 @@ public void generate(final Context ctx) { // Sort field entries and add them back to the class processedFields.sort(Comparator.comparing(field -> field.getVariable(0).getNameAsString())); - primaryTypeDeclaration.getMembers().addAll(lastNonFieldIndex, processedFields); + var indextoAddAt = lastNonFieldIndex; + if (indextoAddAt == -1) { + indextoAddAt = 0; + } + primaryTypeDeclaration.getMembers().addAll(indextoAddAt, processedFields); if (!added.isEmpty()) { Logger.info("Added {} entries to {} that will require manual action to implement: {}", added.size(), this.targetClassSimpleName, added); @@ -147,36 +151,36 @@ public void generate(final Context ctx) { } // Attempt to get a resource location from the field by parsing its initializer - private ResourceLocation extractFieldIdentifier(final FieldDeclaration declaration) { + private Identifier extractFieldIdentifier(final FieldDeclaration declaration) { if (declaration.getVariables().isEmpty()) { throw new IllegalStateException("No variables for " + declaration); } final VariableDeclarator var = declaration.getVariable(0); final Expression initializer = var.getInitializer().orElse(null); if (!(initializer instanceof MethodCallExpr) || ((MethodCallExpr) initializer).getArguments().size() != 1) { - return ResourceLocation.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess } final Expression argument = ((MethodCallExpr) initializer).getArgument(0); if (!(argument instanceof final MethodCallExpr keyInitializer) || keyInitializer.getArguments().size() < 1) { - return ResourceLocation.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess } if (keyInitializer.getArguments().size() == 1) { // method name as namespace - return ResourceLocation.fromNamespaceAndPath(keyInitializer.getNameAsString(), keyInitializer.getArgument(0).asStringLiteralExpr().asString()); + return Identifier.fromNamespaceAndPath(keyInitializer.getNameAsString(), keyInitializer.getArgument(0).asStringLiteralExpr().asString()); } else if (keyInitializer.getArguments().size() == 2) { // (namespace, path) - return ResourceLocation.fromNamespaceAndPath( + return Identifier.fromNamespaceAndPath( keyInitializer.getArgument(0).asStringLiteralExpr().asString(), keyInitializer.getArgument(1).asStringLiteralExpr().asString() ); } else { - return ResourceLocation.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess } } - private FieldDeclaration makeField(final String ownType, final String factoryMethod, final ResourceLocation element) { + private FieldDeclaration makeField(final String ownType, final String factoryMethod, final Identifier element) { final FieldDeclaration fieldDeclaration = new FieldDeclaration(); final VariableDeclarator variable = new VariableDeclarator(StaticJavaParser.parseType("DefaultedRegistryReference"), Types.keyToFieldName(element.getPath())); fieldDeclaration.getVariables().add(variable); @@ -185,7 +189,7 @@ private FieldDeclaration makeField(final String ownType, final String factoryMet return fieldDeclaration; } - public static MethodCallExpr resourceKey(final ResourceLocation location) { + public static MethodCallExpr resourceKey(final Identifier location) { Objects.requireNonNull(location, "location"); final var resourceKey = new NameExpr("ResourceKey"); return switch (location.getNamespace()) { diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/GeneratorMain.java b/generator/src/main/java/org/spongepowered/vanilla/generator/GeneratorMain.java index 6748235dd16..a0bb52beb13 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/GeneratorMain.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/GeneratorMain.java @@ -27,9 +27,10 @@ import com.github.javaparser.utils.Log; import com.mojang.datafixers.util.Pair; import net.minecraft.SharedConstants; -import net.minecraft.Util; import net.minecraft.commands.Commands.CommandSelection; +import net.minecraft.core.HolderLookup; import net.minecraft.core.LayeredRegistryAccess; +import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.resources.RegistryDataLoader; import net.minecraft.server.Bootstrap; @@ -40,6 +41,8 @@ import net.minecraft.server.packs.repository.ServerPacksSource; import net.minecraft.server.packs.resources.CloseableResourceManager; import net.minecraft.server.packs.resources.MultiPackResourceManager; +import net.minecraft.server.permissions.LevelBasedPermissionSet; +import net.minecraft.tags.TagLoader; import net.minecraft.world.level.WorldDataConfiguration; import org.spongepowered.vanilla.generator.item.ItemRegistries; import org.spongepowered.vanilla.generator.world.TagRegistries; @@ -55,6 +58,8 @@ import java.nio.file.Path; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -69,13 +74,13 @@ private GeneratorMain() { /** * The entry point. * - * @param args arguments, expected to be {@code } + * @param args arguments, expected to be {@code } */ public static void main(final String[] args) { Logger.info("Begining bootstrap"); Log.setAdapter(new JavaparserLog()); - if (args.length != 2) { - Logger.error("Invalid arguments. Usage: generator "); + if (args.length != 3) { + Logger.error("Invalid arguments. Usage: generator "); System.exit(1); return; } @@ -86,8 +91,9 @@ public static void main(final String[] args) { // Create a generator context based on arguments final var outputDir = Path.of(args[0]); + final var implOutputDir = Path.of(args[1]); final String licenseHeader; - try (final var reader = Files.newBufferedReader(Path.of(args[1]), StandardCharsets.UTF_8)) { + try (final var reader = Files.newBufferedReader(Path.of(args[2]), StandardCharsets.UTF_8)) { licenseHeader = reader.lines().map(line -> (" * " + line).stripTrailing()).collect(Collectors.joining("\n", "\n", "\n ")); } catch (final IOException ex) { Logger.error("Failed to read license header file!", ex); @@ -96,7 +102,7 @@ public static void main(final String[] args) { } final var dataPacks = GeneratorMain.loadVanillaDatapack(); - final var context = new Context(outputDir, dataPacks.getFirst(), dataPacks.getSecond(), licenseHeader); + final var context = new Context(outputDir, implOutputDir, dataPacks.getFirst(), dataPacks.getSecond(), licenseHeader); Logger.info("Generating data for Minecraft version {}", context.gameVersion()); // Execute every generator @@ -125,37 +131,41 @@ private static Pair loadVanill // Load resource packs, see WorldStem.load // and call to WorldStem.load in net.minecraft.server.Main // We don't currently try to load any datapacks here + final ExecutorService executor = Executors.newCachedThreadPool(); + final var packRepository = ServerPacksSource.createVanillaTrustedRepository(); MinecraftServer.configurePackRepository(packRepository, WorldDataConfiguration.DEFAULT, /* safeMode = */ false, true); - final CloseableResourceManager resourceManager = new MultiPackResourceManager(PackType.SERVER_DATA, packRepository.openAllSelected()); + final CloseableResourceManager rm = new MultiPackResourceManager(PackType.SERVER_DATA, packRepository.openAllSelected()); // WorldLoader.load final LayeredRegistryAccess staticRegistries = RegistryLayer.createRegistryAccess(); - final LayeredRegistryAccess withWorldgen = staticRegistries.replaceFrom( - RegistryLayer.WORLDGEN, - RegistryDataLoader.load(resourceManager, staticRegistries.getAccessForLoading(RegistryLayer.WORLDGEN), RegistryDataLoader.WORLDGEN_REGISTRIES) - ); - final LayeredRegistryAccess withDimensions = withWorldgen.replaceFrom( - RegistryLayer.DIMENSIONS, - RegistryDataLoader.load(resourceManager, staticRegistries.getAccessForLoading(RegistryLayer.DIMENSIONS), RegistryDataLoader.DIMENSION_REGISTRIES) - ); - + List> pendingTags = TagLoader.loadTagsForExistingRegistries(rm, staticRegistries.getLayer(RegistryLayer.STATIC)); + final var wga = staticRegistries.getAccessForLoading(RegistryLayer.WORLDGEN); + List> tl = TagLoader.buildUpdatedLookups(wga, pendingTags); + RegistryAccess.Frozen wgr = RegistryDataLoader.load(rm, tl, RegistryDataLoader.WORLDGEN_REGISTRIES, executor).join(); + List> cl = Stream.concat(tl.stream(), wgr.listRegistries()).toList(); + final LayeredRegistryAccess withWorldGen = staticRegistries.replaceFrom(RegistryLayer.WORLDGEN, wgr); + RegistryAccess.Frozen da = RegistryDataLoader.load(rm, cl, RegistryDataLoader.DIMENSION_REGISTRIES, executor).join(); + final LayeredRegistryAccess withDimensions = withWorldGen.replaceFrom(RegistryLayer.DIMENSIONS, da); + TagLoader.loadTagsForExistingRegistries(rm, withDimensions.getLayer(RegistryLayer.WORLDGEN)); final RegistryAccess.Frozen compositeRegistries = withDimensions.getAccessForLoading(RegistryLayer.RELOADABLE); final var resourcesFuture = ReloadableServerResources.loadResources( - resourceManager, + rm, withDimensions, + pendingTags, packRepository.getRequestedFeatureFlags(), CommandSelection.ALL, - 2, // functionPermissionLevel - Util.backgroundExecutor(), // prepareExecutor + LevelBasedPermissionSet.ALL_PERMISSIONS, // functionPermissionLevel + executor, // prepareExecutor Runnable::run // applyExecutor ).whenComplete((result, ex) -> { if (ex != null) { - resourceManager.close(); + rm.close(); } + executor.shutdown(); }).thenApply(resources -> { - resources.updateRegistryTags(); + resources.updateComponentsAndStaticRegistryTags(); return resources; }); @@ -165,6 +175,7 @@ private static Pair loadVanill try { resources = resourcesFuture.get(); } catch (final InterruptedException | ExecutionException ex) { + executor.shutdown(); Logger.error(ex, "Failed to load registries/datapacks"); System.exit(1); throw new RuntimeException(); diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/MapEntriesValidator.java b/generator/src/main/java/org/spongepowered/vanilla/generator/MapEntriesValidator.java index e77780db0f6..8ca4527daed 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/MapEntriesValidator.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/MapEntriesValidator.java @@ -37,7 +37,7 @@ import com.github.javaparser.ast.expr.StringLiteralExpr; import com.github.javaparser.javadoc.Javadoc; import com.github.javaparser.javadoc.description.JavadocDescription; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.tinylog.Logger; import java.util.ArrayList; @@ -56,7 +56,7 @@ public class MapEntriesValidator implements Generator { private final String targetClassSimpleName; private final Class clazz; private final String registry; - private final Function, Map> mapping; + private final Function, Map> mapping; MapEntriesValidator( final String targetRelativePackage, @@ -64,7 +64,7 @@ public class MapEntriesValidator implements Generator { final Class clazz, final String registry ) { - this(targetRelativePackage, targetClassSimpleName, clazz, registry, map -> (Map) map); + this(targetRelativePackage, targetClassSimpleName, clazz, registry, map -> (Map) map); } public MapEntriesValidator( @@ -72,7 +72,7 @@ public MapEntriesValidator( final String targetClassSimpleName, final Class clazz, final String registry, - final Function, Map> mapping + final Function, Map> mapping ) { this.relativePackageName = targetRelativePackage; this.targetClassSimpleName = targetClassSimpleName; @@ -96,7 +96,7 @@ public void generate(final Context ctx) { primaryTypeDeclaration.setJavadocComment(new Javadoc(JavadocDescription.parseText(Generator.GENERATED_FILE_JAVADOCS))); - final Map map; + final Map map; try { final var f = this.clazz.getDeclaredField(this.registry); f.setAccessible(true); @@ -112,7 +112,7 @@ public void generate(final Context ctx) { // Find index of first field member // Take out all field members from the members list final var members = primaryTypeDeclaration.getMembers(); - final var fields = new HashMap(); + final var fields = new HashMap(); int lastNonFieldIndex = -1; for (final var it = members.listIterator(); it.hasNext();) { final var node = it.next(); @@ -128,10 +128,10 @@ public void generate(final Context ctx) { } // Now, iterate the registry, discovering which fields were added and removed - final var added = new HashSet(); + final var added = new HashSet(); final var processedFields = new ArrayList(map.keySet().size()); - final Set allKeys = new HashSet<>(map.keySet()); - for (final ResourceLocation key : allKeys) { + final Set allKeys = new HashSet<>(map.keySet()); + for (final Identifier key : allKeys) { final FieldDeclaration existing = fields.remove(key); if (existing != null) { processedFields.add(existing); @@ -154,36 +154,36 @@ public void generate(final Context ctx) { } // Attempt to get a resource location from the field by parsing its initializer - private ResourceLocation extractFieldIdentifier(final FieldDeclaration declaration) { + private Identifier extractFieldIdentifier(final FieldDeclaration declaration) { if (declaration.getVariables().isEmpty()) { throw new IllegalStateException("No variables for " + declaration); } final VariableDeclarator var = declaration.getVariable(0); final Expression initializer = var.getInitializer().orElse(null); if (!(initializer instanceof MethodCallExpr) || ((MethodCallExpr) initializer).getArguments().size() != 1) { - return ResourceLocation.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess } final Expression argument = ((MethodCallExpr) initializer).getArgument(0); if (!(argument instanceof final MethodCallExpr keyInitializer) || keyInitializer.getArguments().size() < 1) { - return ResourceLocation.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess } if (keyInitializer.getArguments().size() == 1) { // method name as namespace - return ResourceLocation.fromNamespaceAndPath(keyInitializer.getNameAsString(), keyInitializer.getArgument(0).asStringLiteralExpr().asString()); + return Identifier.fromNamespaceAndPath(keyInitializer.getNameAsString(), keyInitializer.getArgument(0).asStringLiteralExpr().asString()); } else if (keyInitializer.getArguments().size() == 2) { // (namespace, path) - return ResourceLocation.fromNamespaceAndPath( + return Identifier.fromNamespaceAndPath( keyInitializer.getArgument(0).asStringLiteralExpr().asString(), keyInitializer.getArgument(1).asStringLiteralExpr().asString() ); } else { - return ResourceLocation.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess } } - private FieldDeclaration makeField(final String ownType, final String factoryMethod, final ResourceLocation element) { + private FieldDeclaration makeField(final String ownType, final String factoryMethod, final Identifier element) { final FieldDeclaration fieldDeclaration = new FieldDeclaration(); final VariableDeclarator variable = new VariableDeclarator(StaticJavaParser.parseType("DefaultedRegistryReference"), Types.keyToFieldName(element.getPath())); fieldDeclaration.getVariables().add(variable); @@ -192,7 +192,7 @@ private FieldDeclaration makeField(final String ownType, final String factoryMet return fieldDeclaration; } - public static MethodCallExpr resourceKey(final ResourceLocation location) { + public static MethodCallExpr resourceKey(final Identifier location) { Objects.requireNonNull(location, "location"); final var resourceKey = new NameExpr("ResourceKey"); return switch (location.getNamespace()) { diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryEntriesGenerator.java b/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryEntriesGenerator.java index c410eb195dc..97c04fbe46c 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryEntriesGenerator.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryEntriesGenerator.java @@ -28,10 +28,11 @@ import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.flag.FeatureElement; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.flag.FeatureFlags; @@ -108,7 +109,7 @@ public RegistryEntriesGenerator(final String relativePackageName, @Override public String name() { - return "elements of registry " + this.registry.location(); + return "elements of registry " + this.registry.identifier(); } @Override @@ -121,9 +122,9 @@ public void generate(final Context ctx) throws IOException { clazz.addAnnotation(Types.suppressWarnings("unused")); final RegistryScope scopeType; - Registry registry = (Registry) BuiltInRegistries.REGISTRY.get(this.registry.location()); + Registry registry = (Registry) BuiltInRegistries.REGISTRY.get(this.registry.identifier()).map(Holder.Reference::value).orElse(null); if (registry == null) { - registry = ctx.registries().registry(this.registry).orElse(null); + registry = ctx.registries().lookup(this.registry).orElse(null); if (registry == null) { throw new IllegalArgumentException("Unknown registry " + this.registry); } @@ -151,7 +152,7 @@ public void generate(final Context ctx) throws IOException { ctx.compilationUnit(this.relativePackageName, this.targetClassSimpleName); } - private FieldSpec makeField(final String ownType, final TypeName fieldType, final MethodSpec factoryMethod, final ResourceLocation element, @Nullable final FeatureFlagSet featureFlagSet) { + private FieldSpec makeField(final String ownType, final TypeName fieldType, final MethodSpec factoryMethod, final Identifier element, @Nullable final FeatureFlagSet featureFlagSet) { final FieldSpec.Builder builder = FieldSpec.builder(fieldType, Types.keyToFieldName(element.getPath()), Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) @@ -160,7 +161,7 @@ private FieldSpec makeField(final String ownType, final TypeName fieldType, fina if (!featureFlagSet.isSubsetOf(FeatureFlags.VANILLA_SET)) { final var flags = FeatureFlags.REGISTRY.toNames(featureFlagSet).stream().map(rl -> rl.getNamespace().equals("minecraft") ? rl.getPath() : rl.getNamespace() + ":" + rl.getPath()).toArray(); // Use this when new feature flags are introduced -// if (featureFlagSet.contains(FeatureFlags.UPDATE_1_20)) { +// if (featureFlagSet.contains(FeatureFlags.WINTER_DROP)) { // var annotation = AnnotationSpec.builder(ClassName.get("org.spongepowered.api.util.annotation", "Experimental")) // .addMember("value", "$S", flags).build(); // builder.addAnnotation(annotation).build(); diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryEntriesValidator.java b/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryEntriesValidator.java index 8e92db54863..bf6202c06b1 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryEntriesValidator.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryEntriesValidator.java @@ -39,8 +39,8 @@ import com.github.javaparser.javadoc.description.JavadocDescription; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import org.tinylog.Logger; import java.util.ArrayList; @@ -59,7 +59,7 @@ public class RegistryEntriesValidator implements Generator { private final String targetClassSimpleName; private final ResourceKey> registry; private final Predicate filter; - private final Set extraEntries; + private final Set extraEntries; public RegistryEntriesValidator( final String targetRelativePackage, @@ -74,7 +74,7 @@ public RegistryEntriesValidator( final String targetClassSimpleName, final ResourceKey> registry, final Predicate filter, - final Set extraEntries + final Set extraEntries ) { this.relativePackageName = targetRelativePackage; this.targetClassSimpleName = targetClassSimpleName; @@ -85,7 +85,7 @@ public RegistryEntriesValidator( @Override public String name() { - return "elements of registry " + this.registry.location(); + return "elements of registry " + this.registry.identifier(); } @Override @@ -96,12 +96,10 @@ public void generate(final Context ctx) { final var primaryTypeDeclaration = compilationUnit.getPrimaryType() .orElseThrow(() -> new IllegalStateException("Could not find primary type for registry type " + this.targetClassSimpleName)); - Registry registry = ctx.registries().registry(this.registry).orElse(null); + Registry registry = ctx.registries().lookup(this.registry).orElse(null); if (registry == null) { - registry = (Registry) BuiltInRegistries.REGISTRY.get(this.registry.location()); - if (registry == null) { - throw new IllegalArgumentException("Unknown registry " + this.registry); - } + registry = (Registry) BuiltInRegistries.REGISTRY.get(this.registry.identifier()) + .orElseThrow(() -> new IllegalArgumentException("Unknown registry " + this.registry)); } primaryTypeDeclaration.setJavadocComment(new Javadoc(JavadocDescription.parseText(Generator.GENERATED_FILE_JAVADOCS))); @@ -109,7 +107,7 @@ public void generate(final Context ctx) { // Find index of first field member // Take out all field members from the members list final var members = primaryTypeDeclaration.getMembers(); - final var fields = new HashMap(); + final var fields = new HashMap(); int lastNonFieldIndex = -1; for (final var it = members.listIterator(); it.hasNext();) { final var node = it.next(); @@ -125,12 +123,12 @@ public void generate(final Context ctx) { } // Now, iterate the registry, discovering which fields were added and removed - final var added = new HashSet(); + final var added = new HashSet(); final var processedFields = new ArrayList(registry.keySet().size()); - final Set allKeys = new HashSet<>(registry.keySet()); + final Set allKeys = new HashSet<>(registry.keySet()); allKeys.addAll(this.extraEntries); - for (final ResourceLocation key : allKeys) { - if (!this.filter.test(registry.get(key))) { + for (final Identifier key : allKeys) { + if (!this.filter.test(registry.getValue(key))) { continue; } @@ -156,36 +154,36 @@ public void generate(final Context ctx) { } // Attempt to get a resource location from the field by parsing its initializer - private ResourceLocation extractFieldIdentifier(final FieldDeclaration declaration) { + private Identifier extractFieldIdentifier(final FieldDeclaration declaration) { if (declaration.getVariables().isEmpty()) { throw new IllegalStateException("No variables for " + declaration); } final VariableDeclarator var = declaration.getVariable(0); final Expression initializer = var.getInitializer().orElse(null); if (!(initializer instanceof MethodCallExpr) || ((MethodCallExpr) initializer).getArguments().size() != 1) { - return ResourceLocation.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess } final Expression argument = ((MethodCallExpr) initializer).getArgument(0); if (!(argument instanceof final MethodCallExpr keyInitializer) || keyInitializer.getArguments().size() < 1) { - return ResourceLocation.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess } if (keyInitializer.getArguments().size() == 1) { // method name as namespace - return ResourceLocation.fromNamespaceAndPath(keyInitializer.getNameAsString(), keyInitializer.getArgument(0).asStringLiteralExpr().asString()); + return Identifier.fromNamespaceAndPath(keyInitializer.getNameAsString(), keyInitializer.getArgument(0).asStringLiteralExpr().asString()); } else if (keyInitializer.getArguments().size() == 2) { // (namespace, path) - return ResourceLocation.fromNamespaceAndPath( + return Identifier.fromNamespaceAndPath( keyInitializer.getArgument(0).asStringLiteralExpr().asString(), keyInitializer.getArgument(1).asStringLiteralExpr().asString() ); } else { - return ResourceLocation.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess + return Identifier.parse(var.getNameAsString().toLowerCase(Locale.ROOT)); // a best guess } } - private FieldDeclaration makeField(final String ownType, final String factoryMethod, final ResourceLocation element) { + private FieldDeclaration makeField(final String ownType, final String factoryMethod, final Identifier element) { final FieldDeclaration fieldDeclaration = new FieldDeclaration(); final VariableDeclarator variable = new VariableDeclarator(StaticJavaParser.parseType("DefaultedRegistryReference"), Types.keyToFieldName(element.getPath())); fieldDeclaration.getVariables().add(variable); @@ -194,7 +192,7 @@ private FieldDeclaration makeField(final String ownType, final String factoryMet return fieldDeclaration; } - public static MethodCallExpr resourceKey(final ResourceLocation location) { + public static MethodCallExpr resourceKey(final Identifier location) { Objects.requireNonNull(location, "location"); final var resourceKey = new NameExpr("ResourceKey"); return switch (location.getNamespace()) { diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryScope.java b/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryScope.java index cbcbf2c6e9a..28bea4c642e 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryScope.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/RegistryScope.java @@ -63,7 +63,7 @@ void populateRegistryGetter(final Builder builder, final CodeBlock registryType) SERVER { @Override protected CodeBlock registryKeyToReference() { - return CodeBlock.of("asDefaultedReference($T::server)", Types.SPONGE); + return CodeBlock.of("asScopedReference()", Types.SPONGE); } @Override diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/TagGenerator.java b/generator/src/main/java/org/spongepowered/vanilla/generator/TagGenerator.java index 87955f21e49..96b8e6f4a56 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/TagGenerator.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/TagGenerator.java @@ -29,9 +29,10 @@ import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; +import net.minecraft.core.HolderSet; import net.minecraft.core.Registry; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; import java.io.IOException; @@ -78,7 +79,7 @@ public void generate(final Context ctx) throws IOException { clazz.addAnnotation(Types.suppressWarnings("unused")); clazz.addAnnotation(TagGenerator.SCOPE.registryScopeAnnotation()); - final var fieldType = ParameterizedTypeName.get(Types.TAG, this.typeName); + final var fieldType = ParameterizedTypeName.get(Types.DEFAULTED_TAG, this.typeName); final ParameterSpec locationParam = ParameterSpec.builder(Types.RESOURCE_KEY, "key", Modifier.FINAL).build(); final var factoryMethod = MethodSpec.methodBuilder("key") .addModifiers(Modifier.PRIVATE, Modifier.STATIC) @@ -86,15 +87,16 @@ public void generate(final Context ctx) throws IOException { .addParameter(locationParam) .addCode( "return $T.of($T.$L, $N);", - Types.TAG, + Types.DEFAULTED_TAG, Types.REGISTRY_TYPES, this.registryTypeName.toUpperCase(Locale.ROOT), locationParam ).build(); - ctx.registries().registryOrThrow(this.taggedRegistry).getTagNames() - .map(TagKey::location) + ctx.registries().lookupOrThrow(this.taggedRegistry).getTags() + .>map(HolderSet.Named::key) + .map(TagKey::location) .sorted(Comparator.naturalOrder()) .map(v -> this.makeField(this.targetClassSimpleName, fieldType, factoryMethod, v)) .forEachOrdered(clazz::addField); @@ -107,7 +109,7 @@ public void generate(final Context ctx) throws IOException { ctx.compilationUnit(this.relativePackageName, this.targetClassSimpleName); } - private FieldSpec makeField(final String ownType, final TypeName fieldType, final MethodSpec factoryMethod, final ResourceLocation element) { + private FieldSpec makeField(final String ownType, final TypeName fieldType, final MethodSpec factoryMethod, final Identifier element) { return FieldSpec.builder(fieldType, Types.keyToFieldName(element.getPath()), Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("$L.$N($L)", ownType, factoryMethod, Types.resourceKey(element)) .build(); diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/Types.java b/generator/src/main/java/org/spongepowered/vanilla/generator/Types.java index 518cbb2d6da..1a72118cdad 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/Types.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/Types.java @@ -31,7 +31,7 @@ import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.WildcardTypeName; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import java.util.Locale; import java.util.Objects; @@ -57,6 +57,8 @@ final class Types { public static final ClassName DEFAULTED_REGISTRY_TYPE = ClassName.get(Context.BASE_PACKAGE + ".registry", "DefaultedRegistryType"); + public static final ClassName DEFAULTED_TAG = ClassName.get(Context.BASE_PACKAGE + ".tag", "DefaultedTag"); + public static final ClassName REGISTRY = ClassName.get(Context.BASE_PACKAGE + ".registry", "Registry"); public static final ClassName REGISTRY_KEY = ClassName.get(Context.BASE_PACKAGE + ".registry", "RegistryKey"); @@ -109,7 +111,7 @@ static String keyToFieldName(final String key) { }; } - public static CodeBlock resourceKey(final ResourceLocation location) { + public static CodeBlock resourceKey(final Identifier location) { Objects.requireNonNull(location, "location"); return Types.resourceKey(location.getNamespace(), location.getPath()); } diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/item/ItemRegistries.java b/generator/src/main/java/org/spongepowered/vanilla/generator/item/ItemRegistries.java index 0d53abbbc88..ed1abd33a8b 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/item/ItemRegistries.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/item/ItemRegistries.java @@ -25,10 +25,13 @@ package org.spongepowered.vanilla.generator.item; import net.minecraft.core.registries.Registries; +import net.minecraft.resources.Identifier; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.Rarity; -import net.minecraft.world.item.Tiers; +import net.minecraft.world.item.ToolMaterial; +import net.minecraft.world.item.equipment.ArmorMaterials; +import org.spongepowered.vanilla.generator.ClassFieldsValidator; import org.spongepowered.vanilla.generator.Context; import org.spongepowered.vanilla.generator.EnumEntriesValidator; import org.spongepowered.vanilla.generator.Generator; @@ -37,6 +40,7 @@ import org.spongepowered.vanilla.generator.RegistryScope; import java.util.List; +import java.util.Locale; public class ItemRegistries { @@ -107,6 +111,13 @@ public static List itemRegistries(final Context context) { $ -> true, RegistryScope.SERVER ), + new RegistryEntriesGenerator<>( + "data.type", + "ItemActionTypes", + "ITEM_ACTION_TYPE", + context.relativeClass("data.type", "ItemActionType"), + Registries.CONSUME_EFFECT_TYPE + ), new EnumEntriesValidator<>( "entity.display", "ItemDisplayTypes", @@ -114,19 +125,16 @@ public static List itemRegistries(final Context context) { "getSerializedName", "sponge" ), - new EnumEntriesValidator<>( + new ClassFieldsValidator<>( "data.type", "ItemTiers", - Tiers.class, - "name", - "sponge" + ToolMaterial.class ), - new RegistryEntriesGenerator<>( + new ClassFieldsValidator<>( "data.type", "ArmorMaterials", - "ARMOR_MATERIAL", - context.relativeClass("data.type", "ArmorMaterial"), - Registries.ARMOR_MATERIAL + ArmorMaterials.class, + (s) -> Identifier.withDefaultNamespace(s.toLowerCase(Locale.ROOT)) ) ); } diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/world/TagRegistries.java b/generator/src/main/java/org/spongepowered/vanilla/generator/world/TagRegistries.java index 02340f3ea7b..a4a2ab41155 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/world/TagRegistries.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/world/TagRegistries.java @@ -77,15 +77,35 @@ public static List tagRegistries(final Context context) { Registries.ENCHANTMENT, context.relativeClass("item.enchantment", "EnchantmentType"), "tag", - "EnchantmenTypeTags" + "EnchantmentTypeTags" ), - new TagGenerator( "DAMAGE_TYPE", Registries.DAMAGE_TYPE, context.relativeClass("event.cause.entity.damage", "DamageType"), "tag", "DamageTypeTags" + ), + new TagGenerator( + "BANNER_PATTERN_SHAPE", + Registries.BANNER_PATTERN, + context.relativeClass("data.type", "BannerPatternShape"), + "tag", + "BannerPatternShapeTags" + ), + new TagGenerator( + "ART_TYPE", + Registries.PAINTING_VARIANT, + context.relativeClass("data.type", "ArtType"), + "tag", + "ArtTypeTags" + ), + new TagGenerator( + "STRUCTURE", + Registries.STRUCTURE, + context.relativeClass("world.generation.structure", "Structure"), + "tag", + "StructureTags" ) ); } diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/world/entities/EntityRegistries.java b/generator/src/main/java/org/spongepowered/vanilla/generator/world/entities/EntityRegistries.java index d3dee48550a..cc947bd5e67 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/world/entities/EntityRegistries.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/world/entities/EntityRegistries.java @@ -25,21 +25,23 @@ package org.spongepowered.vanilla.generator.world.entities; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.entity.Display; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.EquipmentSlotGroup; import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.entity.animal.Fox; -import net.minecraft.world.entity.animal.MushroomCow; -import net.minecraft.world.entity.animal.Panda; -import net.minecraft.world.entity.animal.Parrot; -import net.minecraft.world.entity.animal.Rabbit; -import net.minecraft.world.entity.animal.TropicalFish; -import net.minecraft.world.entity.animal.horse.Llama; -import net.minecraft.world.entity.animal.horse.Markings; -import net.minecraft.world.entity.animal.horse.Variant; -import net.minecraft.world.entity.monster.SpellcasterIllager; -import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import net.minecraft.world.entity.animal.cow.MushroomCow; +import net.minecraft.world.entity.animal.equine.Llama; +import net.minecraft.world.entity.animal.equine.Markings; +import net.minecraft.world.entity.animal.equine.Variant; +import net.minecraft.world.entity.animal.fish.Salmon; +import net.minecraft.world.entity.animal.fish.TropicalFish; +import net.minecraft.world.entity.animal.fox.Fox; +import net.minecraft.world.entity.animal.panda.Panda; +import net.minecraft.world.entity.animal.parrot.Parrot; +import net.minecraft.world.entity.animal.rabbit.Rabbit; +import net.minecraft.world.entity.monster.illager.SpellcasterIllager; import org.spongepowered.vanilla.generator.Context; import org.spongepowered.vanilla.generator.EnumEntriesValidator; import org.spongepowered.vanilla.generator.Generator; @@ -60,13 +62,6 @@ public static List enumEntries(final Context context) { "getSerializedName", "sponge" ), - new EnumEntriesValidator<>( - "data.type", - "BoatTypes", - Boat.Type.class, - "getSerializedName", - "sponge" - ), new EnumEntriesValidator<>( "data.type", "PandaGenes", @@ -98,14 +93,14 @@ public static List enumEntries(final Context context) { new EnumEntriesValidator<>( "data.type", "MooshroomTypes", - MushroomCow.MushroomType.class, + MushroomCow.Variant.class, "getSerializedName", "sponge" ), new EnumEntriesValidator<>( "data.type", "FoxTypes", - Fox.Type.class, + Fox.Variant.class, "getSerializedName", "sponge" ), @@ -165,6 +160,13 @@ public static List enumEntries(final Context context) { "getSerializedName", "sponge" ), + new EnumEntriesValidator<>( + "item.inventory.equipment", + "EquipmentConditions", + EquipmentSlotGroup.class, + "getSerializedName", + "sponge" + ), new EnumEntriesValidator<>( "item.inventory.equipment", "EquipmentGroups", @@ -178,6 +180,20 @@ public static List enumEntries(final Context context) { EquipmentSlot.class, "getSerializedName", "sponge" + ), + new EnumEntriesValidator<>( + "data.type", + "SalmonSizes", + Salmon.Variant.class, + "getSerializedName", + "sponge" + ), + new EnumEntriesValidator<>( + "data.type", + "AxolotlVariants", + Axolotl.Variant.class, + "getSerializedName", + "sponge" ) ); } @@ -189,7 +205,7 @@ public static List registryEntries(final Context context) { "EntityTypes", Registries.ENTITY_TYPE, $ -> true, - Set.of(ResourceLocation.fromNamespaceAndPath("sponge", "human")) // Sponge's Human type is an extra addition + Set.of(Identifier.fromNamespaceAndPath("sponge", "human")) // Sponge's Human type is an extra addition ), new RegistryEntriesGenerator<>( "data.type", @@ -225,9 +241,45 @@ public static List registryEntries(final Context context) { new RegistryEntriesGenerator<>( "data.type", "WolfVariants", - "WOLF_VAIRANT", + "WOLF_VARIANT", context.relativeClass("data.type", "WolfVariant"), Registries.WOLF_VARIANT + ), + new RegistryEntriesGenerator<>( + "data.type", + "PigTypes", + "PIG_TYPE", + context.relativeClass("data.type", "PigType"), + Registries.PIG_VARIANT, + $ -> true, + RegistryScope.SERVER + ), + new RegistryEntriesGenerator<>( + "data.type", + "CowVariants", + "COW_VARIANT", + context.relativeClass("data.type", "CowVariant"), + Registries.COW_VARIANT, + $ -> true, + RegistryScope.SERVER + ), + new RegistryEntriesGenerator<>( + "data.type", + "ChickenVariants", + "CHICKEN_VARIANT", + context.relativeClass("data.type", "ChickenVariant"), + Registries.CHICKEN_VARIANT, + $-> true, + RegistryScope.SERVER + ), + new RegistryEntriesGenerator<>( + "data.type", + "WolfSoundVariants", + "WOLF_SOUND_VARIANT", + context.relativeClass("data.type", "WolfSoundVariant"), + Registries.WOLF_SOUND_VARIANT, + $ -> true, + RegistryScope.SERVER ) ); } diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/world/level/LevelDataRegistries.java b/generator/src/main/java/org/spongepowered/vanilla/generator/world/level/LevelDataRegistries.java index 2116c2aee2c..ebd1185f235 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/world/level/LevelDataRegistries.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/world/level/LevelDataRegistries.java @@ -24,39 +24,23 @@ */ package org.spongepowered.vanilla.generator.world.level; -import com.google.common.base.CaseFormat; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.GameRules; import org.spongepowered.vanilla.generator.Context; import org.spongepowered.vanilla.generator.Generator; -import org.spongepowered.vanilla.generator.MapEntriesValidator; import org.spongepowered.vanilla.generator.RegistryEntriesGenerator; import org.spongepowered.vanilla.generator.RegistryEntriesValidator; import org.spongepowered.vanilla.generator.RegistryScope; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.function.BiConsumer; public class LevelDataRegistries { public static List levelDataRegistries(final Context context) { return List.of( - new MapEntriesValidator<>( + new RegistryEntriesValidator<>( "world.gamerule", "GameRules", - GameRules.class, - "GAME_RULE_TYPES", - map -> { - final Map out = new HashMap<>(map.size()); - map.forEach((BiConsumer) (k, v) -> { - var key = (GameRules.Key) k; - out.put(ResourceLocation.fromNamespaceAndPath("sponge", CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, key.getId())), v); - }); - return out; - } + Registries.GAME_RULE ), new RegistryEntriesGenerator<>( "map.decoration", @@ -70,7 +54,9 @@ public static List levelDataRegistries(final Context context) { "Biomes", "BIOME", context.relativeClass("world.biome", "Biome"), - Registries.BIOME + Registries.BIOME, + a -> true, + RegistryScope.SERVER ), new RegistryEntriesGenerator<>( "world.generation.carver", @@ -101,7 +87,7 @@ public static List levelDataRegistries(final Context context) { "FLAT_GENERATOR_CONFIG", context.relativeClass("world.generation.config.flat", "FlatGeneratorConfig"), Registries.FLAT_LEVEL_GENERATOR_PRESET, - a -> true, RegistryScope.GAME + a -> true, RegistryScope.SERVER ), new RegistryEntriesGenerator<>( "world.generation.config.noise", @@ -156,14 +142,6 @@ public static List levelDataRegistries(final Context context) { Registries.PROCESSOR_LIST, $ -> true, RegistryScope.SERVER ), - new RegistryEntriesGenerator<>( - "world.generation.structure.jigsaw", - "ProcessorTypes", - "PROCESSOR_TYPE", - context.relativeClass("world.generation.structure.jigsaw", "ProcessorType"), - Registries.STRUCTURE_PROCESSOR, - $ -> true, RegistryScope.GAME - ), new RegistryEntriesGenerator<>( "world.generation.feature", "PlacedFeatures", @@ -219,6 +197,15 @@ public static List levelDataRegistries(final Context context) { "FLUID_TYPE", context.relativeClass("fluid", "FluidType"), Registries.FLUID + ), + new RegistryEntriesGenerator<>( + "world.server", + "WorldArchetypeTypes", + "WORLD_ARCHETYPE_TYPE", + context.relativeClass("world.server", "WorldArchetypeType"), + Registries.LEVEL_STEM, + $ -> true, + RegistryScope.SERVER ) ); } diff --git a/generator/src/main/java/org/spongepowered/vanilla/generator/world/level/block/BlockRegistries.java b/generator/src/main/java/org/spongepowered/vanilla/generator/world/level/block/BlockRegistries.java index 33ea29252df..8ebfca0004f 100644 --- a/generator/src/main/java/org/spongepowered/vanilla/generator/world/level/block/BlockRegistries.java +++ b/generator/src/main/java/org/spongepowered/vanilla/generator/world/level/block/BlockRegistries.java @@ -25,11 +25,17 @@ package org.spongepowered.vanilla.generator.world.level.block; import net.minecraft.core.registries.Registries; +import net.minecraft.world.level.Explosion; +import net.minecraft.world.level.block.CopperGolemStatueBlock; +import net.minecraft.world.level.block.WeatheringCopper; import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerState; import net.minecraft.world.level.block.state.properties.BambooLeaves; -import net.minecraft.world.level.block.state.properties.DripstoneThickness; +import net.minecraft.world.level.block.state.properties.CreakingHeartState; import net.minecraft.world.level.block.state.properties.NoteBlockInstrument; import net.minecraft.world.level.block.state.properties.SculkSensorPhase; +import net.minecraft.world.level.block.state.properties.SideChainPart; +import net.minecraft.world.level.block.state.properties.SpeleothemThickness; +import net.minecraft.world.level.block.state.properties.TestBlockMode; import net.minecraft.world.level.block.state.properties.Tilt; import org.spongepowered.vanilla.generator.BlockStateDataProviderGenerator; import org.spongepowered.vanilla.generator.BlockStatePropertiesGenerator; @@ -63,7 +69,7 @@ public static List enumRegistries(final Context context) { new EnumEntriesValidator<>( "data.type", "DripstoneSegments", - DripstoneThickness.class, + SpeleothemThickness.class, "getSerializedName", "sponge" ), @@ -87,6 +93,27 @@ public static List enumRegistries(final Context context) { TrialSpawnerState.class, "getSerializedName", "sponge" + ), + new EnumEntriesValidator<>( + "data.type", + "CopperGolemPoses", + CopperGolemStatueBlock.Pose.class, + "getSerializedName", + "sponge" + ), + new EnumEntriesValidator<>( + "data.type", + "SideChains", + SideChainPart.class, + "getSerializedName", + "sponge" + ), + new EnumEntriesValidator<>( + "data.type", + "CopperOxidations", + WeatheringCopper.WeatherState.class, + "getSerializedName", + "sponge" ) ); } @@ -118,6 +145,27 @@ public static List registries(final Context context) { Registries.BANNER_PATTERN, $ -> true, RegistryScope.SERVER + ), + new EnumEntriesValidator<>( + "world.explosion", + "ExplosionBlockInteractions", + Explosion.BlockInteraction.class, + "name", + "sponge" + ), + new EnumEntriesValidator<>( + "data.type", + "TestBlockModes", + TestBlockMode.class, + "getSerializedName", + "sponge" + ), + new EnumEntriesValidator<>( + "data.type", + "CreakingHeartStates", + CreakingHeartState.class, + "getSerializedName", + "sponge" ) ); } diff --git a/gradle.properties b/gradle.properties index 332bf9cd6d7..3b4e495e552 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,10 +11,10 @@ mixinConfigs=mixins.sponge.accessors.json,mixins.sponge.api.json,mixins.sponge.c mixins.sponge.entityactivation.json,mixins.sponge.exploit.json,mixins.sponge.inventory.json,mixins.sponge.movementcheck.json,\ mixins.sponge.tracker.json,mixins.sponge.ipforward.json,mixins.sponge.optimization.json,mixins.sponge.test.json -minecraftVersion=1.21.1 -recommendedVersion=4-SNAPSHOT +minecraftVersion=26.2 +recommendedVersion=0-SNAPSHOT org.gradle.dependency.verification.console=verbose org.gradle.jvmargs=-Xss4m org.gradle.parallel=true -org.gradle.caching=false \ No newline at end of file +org.gradle.caching=falser diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 16d0393b334..6c7b2736289 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,22 +4,19 @@ format = { version = "1.1" } # Implementation-only dependencies, or dependencies that use a different version than in API # See SpongeAPI's gradle/libs.versions.toml for many dependency declarations that Sponge also uses [versions] -asm = "9.8" -log4j = "2.22.1" -forgeAutoRenamingTool = "1.0.6" +asm = "9.10.1" +log4j = "2.25.2" mixin = "0.8.7" -mixinextras = "0.5.3" -modlauncher = "10.2.3" -neo-modlauncher = "11.0.5" -securemodules = "2.2.22" -securejarhandler = "3.0.8" -jarjar = "0.3.26" +mixinextras = "0.5.4" +modlauncher = "10.2.6" +securemodules = "2.2.24" +fancymodloader = "11.0.15" +jarjar = "0.4.2" guava = "32.1.2-jre" -mockito = "5.11.0" jline = "3.25.1" tinylog = "2.7.0" vineflower = "1.11.1" -jacoco = "0.8.13" +jacoco = "0.8.14" [libraries] # common @@ -34,20 +31,17 @@ log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "lo log4j-jpl = { module = "org.apache.logging.log4j:log4j-jpl", version.ref = "log4j" } log4j-slf4j2 = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" } modlauncher = { module = "net.minecraftforge:modlauncher", version.ref = "modlauncher" } -neo-modlauncher = { module = "cpw.mods:modlauncher", version.ref = "neo-modlauncher" } securemodules = { module = "net.minecraftforge:securemodules", version.ref = "securemodules" } -securejarhandler = { module = "cpw.mods:securejarhandler", version.ref = "securejarhandler" } +fancymodloader = { module = "net.neoforged.fancymodloader:loader", version.ref = "fancymodloader" } tinylog-api = { module = "org.tinylog:tinylog-api", version.ref = "tinylog" } tinylog-impl = { module = "org.tinylog:tinylog-impl", version.ref = "tinylog" } tinylog-slf4j = { module = "org.tinylog:slf4j-tinylog", version.ref = "tinylog" } +accessWidener = { module = "net.fabricmc:access-widener", version = "2.1.0" } # generator javaparser = { module = "com.github.javaparser:javaparser-core", version = "3.25.9" } javapoet = { module = "com.squareup:javapoet", version = "1.13.0" } -# modlauncher-transformers -accessWidener = { module = "net.fabricmc:access-widener", version = "2.1.0" } - # Sponge adventure-serializerAnsi = { module = "net.kyori:adventure-text-serializer-ansi" } # version inherited from bom in API catalog adventure-serializerConfigurate4 = { module = "net.kyori:adventure-serializer-configurate4" } # version inherited from bom in API catalog @@ -57,12 +51,9 @@ mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" } mixinextras-common = { module = "io.github.llamalad7:mixinextras-common", version.ref = "mixinextras" } mixinextras-forge = { module = "io.github.llamalad7:mixinextras-forge", version.ref = "mixinextras" } mixinextras-neo = { module = "io.github.llamalad7:mixinextras-neoforge", version.ref = "mixinextras" } -mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" } -mockito-junitJupiter = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" } jacoco-core = { module = "org.jacoco:org.jacoco.core", version.ref = "jacoco" } # vanilla -forgeAutoRenamingTool = { module = "net.minecraftforge:ForgeAutoRenamingTool", version.ref = "forgeAutoRenamingTool" } jarjar-fs = { module = "net.minecraftforge:JarJarFileSystems", version.ref = "jarjar" } jline-reader = { module = "org.jline:jline-reader", version.ref = "jline" } jline-terminal = { module = "org.jline:jline-terminal", version.ref = "jline" } @@ -79,5 +70,5 @@ blossom = { id = "net.kyori.blossom", version = "2.1.0" } indra-licenserSpotless = { id = "net.kyori.indra.licenser.spotless", version = "3.1.3" } shadow = { id = "com.gradleup.shadow", version = "9.0.2" } versions = { id = "com.github.ben-manes.versions", version = "0.51.0" } -forgeGradle = { id = "net.minecraftforge.gradle", version = "6.0.47" } -modDevGradle = { id = "net.neoforged.moddev", version = "2.0.78" } +forgeGradle = { id = "net.minecraftforge.gradle", version = "7.0.34" } +modDevGradle = { id = "net.neoforged.moddev", version = "2.0.144" } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 4093ef7ce53..69f38e456e1 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -6,6 +6,19 @@ + + + + + + + + + + + + + @@ -21,6 +34,8 @@ + + @@ -33,6 +48,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -54,6 +96,24 @@ + + + + + + + + + + + + + + + + + + @@ -162,6 +222,9 @@ + + + @@ -178,10 +241,29 @@ + + + + + + + + + + + + + + + + + + + @@ -190,6 +272,11 @@ + + + + + @@ -218,6 +305,9 @@ + + + @@ -234,10 +324,29 @@ + + + + + + + + + + + + + + + + + + + @@ -246,11 +355,21 @@ + + + + + + + + + + @@ -291,6 +410,11 @@ + + + + + @@ -306,6 +430,11 @@ + + + + + @@ -346,6 +475,11 @@ + + + + + @@ -376,6 +510,17 @@ + + + + + + + + + + + @@ -384,6 +529,14 @@ + + + + + + + + @@ -395,6 +548,17 @@ + + + + + + + + + + + @@ -414,6 +578,17 @@ + + + + + + + + + + + @@ -422,6 +597,14 @@ + + + + + + + + @@ -536,6 +719,22 @@ + + + + + + + + + + + + + + + + @@ -546,6 +745,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -659,6 +884,19 @@ + + + + + + + + + + + + + @@ -700,6 +938,11 @@ + + + + + @@ -716,10 +959,29 @@ + + + + + + + + + + + + + + + + + + + @@ -736,6 +998,14 @@ + + + + + + + + @@ -800,6 +1070,14 @@ + + + + + + + + @@ -816,6 +1094,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -840,6 +1147,14 @@ + + + + + + + + @@ -856,6 +1171,14 @@ + + + + + + + + @@ -896,6 +1219,11 @@ + + + + + @@ -906,6 +1234,21 @@ + + + + + + + + + + + + + + + @@ -943,11 +1286,24 @@ + + + + + + + + + + + + + @@ -964,6 +1320,14 @@ + + + + + + + + @@ -1014,6 +1378,9 @@ + + + @@ -1023,6 +1390,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1063,6 +1457,21 @@ + + + + + + + + + + + + + + + @@ -1124,6 +1533,22 @@ + + + + + + + + + + + + + + + + @@ -1142,6 +1567,11 @@ + + + + + @@ -1150,11 +1580,24 @@ + + + + + + + + + + + + + @@ -1237,6 +1680,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -1269,6 +1733,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1291,6 +1784,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1315,6 +1890,11 @@ + + + + + @@ -1348,6 +1928,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1370,28 +2026,143 @@ - - - + + + - - + + - - - + + + - - + + - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1405,6 +2176,14 @@ + + + + + + + + @@ -1535,6 +2314,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -1575,6 +2375,14 @@ + + + + + + + + @@ -1607,6 +2415,14 @@ + + + + + + + + @@ -1719,6 +2535,22 @@ + + + + + + + + + + + + + + + + @@ -1735,6 +2567,14 @@ + + + + + + + + @@ -1743,6 +2583,14 @@ + + + + + + + + @@ -1812,6 +2660,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -1849,6 +2720,14 @@ + + + + + + + + @@ -1963,6 +2842,9 @@ + + + @@ -1991,6 +2873,11 @@ + + + + + @@ -2015,6 +2902,11 @@ + + + + + @@ -2039,6 +2931,11 @@ + + + + + @@ -2070,6 +2967,22 @@ + + + + + + + + + + + + + + + + @@ -2078,6 +2991,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2086,6 +3033,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2094,6 +3119,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2102,11 +3161,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2115,6 +3223,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2123,6 +3265,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2131,6 +3307,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2145,43 +3379,147 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - - - + + - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2190,6 +3528,14 @@ + + + + + + + + @@ -2242,6 +3588,14 @@ + + + + + + + + @@ -2266,6 +3620,14 @@ + + + + + + + + @@ -2281,6 +3643,11 @@ + + + + + @@ -2369,6 +3736,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2393,6 +3792,22 @@ + + + + + + + + + + + + + + + + @@ -2401,6 +3816,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2441,6 +3887,22 @@ + + + + + + + + + + + + + + + + @@ -2462,6 +3924,22 @@ + + + + + + + + + + + + + + + + @@ -2486,6 +3964,22 @@ + + + + + + + + + + + + + + + + @@ -2519,6 +4013,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2535,6 +4069,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2560,6 +4131,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2582,6 +4193,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2607,6 +4258,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2629,6 +4320,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2654,6 +4417,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2679,6 +4482,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2704,12 +4547,52 @@ - - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2729,6 +4612,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2740,6 +4663,14 @@ + + + + + + + + @@ -2832,6 +4763,14 @@ + + + + + + + + @@ -2892,6 +4831,11 @@ + + + + + @@ -2900,6 +4844,14 @@ + + + + + + + + @@ -2954,6 +4906,14 @@ + + + + + + + + @@ -2973,6 +4933,14 @@ + + + + + + + + @@ -2992,6 +4960,14 @@ + + + + + + + + @@ -3012,10 +4988,26 @@ + + + + + + + + + + + + + + + + @@ -3024,6 +5016,14 @@ + + + + + + + + @@ -3079,10 +5079,32 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -3122,10 +5144,32 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -3177,6 +5221,25 @@ + + + + + + + + + + + + + + + + + + + @@ -3185,6 +5248,16 @@ + + + + + + + + + + @@ -3193,6 +5266,14 @@ + + + + + + + + @@ -3213,10 +5294,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3265,12 +5391,170 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3321,12 +5605,170 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + @@ -3377,45 +5819,203 @@ - - - + + + - - + + - - - + + + - - + + - - + + + + - - + + + + - - - + + + - - + + - - + + + + - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3469,6 +6069,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3492,6 +6216,14 @@ + + + + + + + + @@ -3549,12 +6281,170 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3605,12 +6495,178 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + @@ -3661,22 +6717,196 @@ - - - + + + - - + + - - - + + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3696,6 +6926,16 @@ + + + + + + + + + + @@ -3715,6 +6955,17 @@ + + + + + + + + + + + @@ -3731,6 +6982,19 @@ + + + + + + + + + + + + + @@ -3762,6 +7026,9 @@ + + + @@ -3779,6 +7046,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -3849,6 +7139,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -3865,6 +7179,21 @@ + + + + + + + + + + + + + + + @@ -3894,17 +7223,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3913,6 +7322,14 @@ + + + + + + + + @@ -3929,6 +7346,19 @@ + + + + + + + + + + + + + @@ -3937,6 +7367,19 @@ + + + + + + + + + + + + + @@ -3945,198 +7388,887 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + + + - - - + + + - - + + + + - - + + + + - - - + + + - - - + + + - - + + - - - + + + - - + + - - + + + + - - + + - - - + + + - - + + - - + + + + - - + + - - - + + + - - + + - - + + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + + + - - - + + + - - + + - - - + + + - - + + + + @@ -4147,6 +8279,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4155,6 +8323,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4315,6 +8511,11 @@ + + + + + @@ -4417,6 +8618,22 @@ + + + + + + + + + + + + + + + + @@ -4446,6 +8663,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -4542,6 +8780,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -4552,6 +8810,11 @@ + + + + + @@ -4607,6 +8870,14 @@ + + + + + + + + @@ -4658,6 +8929,11 @@ + + + + + @@ -4706,6 +8982,14 @@ + + + + + + + + @@ -4726,6 +9010,11 @@ + + + + + @@ -4758,6 +9047,14 @@ + + + + + + + + @@ -4790,6 +9087,14 @@ + + + + + + + + @@ -4810,6 +9115,11 @@ + + + + + @@ -4880,11 +9190,21 @@ + + + + + + + + + + @@ -4951,6 +9271,14 @@ + + + + + + + + @@ -4959,6 +9287,22 @@ + + + + + + + + + + + + + + + + @@ -4997,11 +9341,21 @@ + + + + + + + + + + @@ -5052,6 +9406,14 @@ + + + + + + + + @@ -5060,6 +9422,22 @@ + + + + + + + + + + + + + + + + @@ -5089,6 +9467,22 @@ + + + + + + + + + + + + + + + + @@ -5110,6 +9504,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -5145,6 +9563,11 @@ + + + + + @@ -5201,6 +9624,19 @@ + + + + + + + + + + + + + @@ -5229,6 +9665,11 @@ + + + + + @@ -5325,6 +9766,9 @@ + + + @@ -5391,9 +9835,15 @@ + + + + + + @@ -5452,6 +9902,17 @@ + + + + + + + + + + + @@ -5582,6 +10043,11 @@ + + + + + @@ -5767,6 +10233,11 @@ + + + + + @@ -5775,6 +10246,11 @@ + + + + + @@ -5788,6 +10264,11 @@ + + + + + @@ -5796,6 +10277,11 @@ + + + + + @@ -5860,6 +10346,19 @@ + + + + + + + + + + + + + @@ -5871,6 +10370,14 @@ + + + + + + + + @@ -5881,6 +10388,22 @@ + + + + + + + + + + + + + + + + @@ -5889,6 +10412,14 @@ + + + + + + + + @@ -5897,6 +10428,24 @@ + + + + + + + + + + + + + + + + + + @@ -5905,6 +10454,14 @@ + + + + + + + + @@ -5913,12 +10470,38 @@ + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + @@ -5929,16 +10512,34 @@ + + + + + + + + + + + + + + + + + + @@ -5955,6 +10556,14 @@ + + + + + + + + @@ -5979,6 +10588,14 @@ + + + + + + + + @@ -6106,6 +10723,17 @@ + + + + + + + + + + + @@ -6165,6 +10793,14 @@ + + + + + + + + @@ -6173,6 +10809,14 @@ + + + + + + + + @@ -6189,6 +10833,14 @@ + + + + + + + + @@ -6264,6 +10916,14 @@ + + + + + + + + @@ -6283,6 +10943,14 @@ + + + + + + + + @@ -6302,6 +10970,14 @@ + + + + + + + + @@ -6317,6 +10993,17 @@ + + + + + + + + + + + @@ -6334,6 +11021,14 @@ + + + + + + + + @@ -6353,6 +11048,22 @@ + + + + + + + + + + + + + + + + @@ -6405,6 +11116,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6434,6 +11179,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6486,6 +11262,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -6538,6 +11337,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -6590,6 +11412,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6642,6 +11495,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6694,6 +11624,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6746,6 +11707,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6796,6 +11814,14 @@ + + + + + + + + @@ -6882,6 +11908,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6898,6 +11959,11 @@ + + + + + @@ -6951,6 +12017,19 @@ + + + + + + + + + + + + + @@ -6959,6 +12038,11 @@ + + + + + @@ -7007,6 +12091,14 @@ + + + + + + + + @@ -7020,6 +12112,11 @@ + + + + + @@ -7076,6 +12173,19 @@ + + + + + + + + + + + + + @@ -7089,6 +12199,11 @@ + + + + + @@ -7145,6 +12260,24 @@ + + + + + + + + + + + + + + + + + + @@ -7185,6 +12318,14 @@ + + + + + + + + @@ -7265,6 +12406,22 @@ + + + + + + + + + + + + + + + + @@ -7286,6 +12443,16 @@ + + + + + + + + + + @@ -7321,6 +12488,16 @@ + + + + + + + + + + @@ -7538,6 +12715,22 @@ + + + + + + + + + + + + + + + + @@ -7611,6 +12804,16 @@ + + + + + + + + + + @@ -7633,6 +12836,21 @@ + + + + + + + + + + + + + + + @@ -7665,6 +12883,19 @@ + + + + + + + + + + + + + @@ -7681,6 +12912,14 @@ + + + + + + + + @@ -7697,6 +12936,14 @@ + + + + + + + + @@ -7717,6 +12964,11 @@ + + + + + @@ -7781,6 +13033,11 @@ + + + + + @@ -7837,6 +13094,19 @@ + + + + + + + + + + + + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baab..61285a659d1 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 207c4600bb1..8e61ef12590 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=f1771298a70f6db5a29daf62378c4e18a17fc33c9ba6b14362e0cdf40610380d -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip +distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a93670..adff685a034 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index db3a6ac207e..c4bdd3ab8e3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/mise.toml b/mise.toml new file mode 100644 index 00000000000..76843a8e48b --- /dev/null +++ b/mise.toml @@ -0,0 +1,3 @@ +[tools] +gradle = "9.3.1" +java = "temurin-25" diff --git a/modlauncher-transformers/build.gradle.kts b/modlauncher-transformers/build.gradle.kts deleted file mode 100644 index 5ddf331a743..00000000000 --- a/modlauncher-transformers/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -dependencies { - implementation(libs.accessWidener) { - exclude(group="org.apache.logging.log4j") - } - - compileOnly(libs.log4j.api) - compileOnly(libs.neo.modlauncher) { - exclude(group = "org.ow2.asm") - exclude(group = "org.apache.logging.log4j") - } - - compileOnly(libs.joptSimple) - compileOnly(libs.asm.commons) - compileOnly(apiLibs.checkerQual) - - // Optional - compileOnly(libs.jacoco.core) -} - -tasks { - jar { - manifest { - attributes("Automatic-Module-Name" to "sponge.modlauncher_transformers") - } - } -} diff --git a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/AccessWidenerTransformationService.java b/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/AccessWidenerTransformationService.java deleted file mode 100644 index 2d82371fc13..00000000000 --- a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/AccessWidenerTransformationService.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.transformers.modlauncher; - -import cpw.mods.modlauncher.api.IEnvironment; -import cpw.mods.modlauncher.api.ITransformationService; -import cpw.mods.modlauncher.api.ITransformer; -import cpw.mods.modlauncher.api.ITransformerActivity; -import cpw.mods.modlauncher.api.ITransformerVotingContext; -import cpw.mods.modlauncher.api.TargetType; -import cpw.mods.modlauncher.api.TransformerVoteResult; -import cpw.mods.modlauncher.api.TypesafeMap; -import joptsimple.OptionSpec; -import joptsimple.OptionSpecBuilder; -import net.fabricmc.accesswidener.AccessWidener; -import net.fabricmc.accesswidener.AccessWidenerClassVisitor; -import net.fabricmc.accesswidener.AccessWidenerReader; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.framework.qual.DefaultQualifier; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.function.Supplier; - -@DefaultQualifier(NonNull.class) -public class AccessWidenerTransformationService implements ITransformationService { - - public static final String NAME = "access_widener"; - public static final String ACCESS_WIDENER_EXTENSION = "accesswidener"; - public static final Supplier> - INSTANCE = IEnvironment.buildKey("sponge:aw", AccessWidenerTransformationService.class); - static final Logger LOGGER = LogManager.getLogger(); - - private final AccessWidener widener = new AccessWidener(); - private final AccessWidenerReader reader = new AccessWidenerReader(this.widener); - private @MonotonicNonNull OptionSpec configSpec; - - @NonNull - @Override - public String name() { - return AccessWidenerTransformationService.NAME; - } - - @Override - public void onLoad(final IEnvironment env, final Set otherServices) { - env.computePropertyIfAbsent(AccessWidenerTransformationService.INSTANCE.get(), k -> this); - } - - @Override - public void arguments(final BiFunction argumentBuilder) { - this.configSpec = argumentBuilder.apply("config", "An AW file to apply at runtime") - .withOptionalArg() - .withValuesSeparatedBy(","); - } - - @Override - public void argumentValues(final OptionResult option) { - if (this.configSpec != null) { - for (final String value : option.values(this.configSpec)) { - final URL resource = AccessWidenerTransformationService.class.getClassLoader().getResource(value); - if (resource == null) { - throw new IllegalStateException("Could not locate AW resource specified on command line " + value); - } - this.offerResource(resource, "command-line"); - } - } - ITransformationService.super.argumentValues(option); - } - - @Override - public void initialize(final IEnvironment environment) { - } - - @Override - public @NonNull List> transformers() { - return Collections.singletonList(new AWTransformer(this.widener)); - } - - // register additional AWs with the transformation service - public void offerResource(final URL resource, final String name) { - if (resource.getFile().endsWith(AccessWidenerTransformationService.ACCESS_WIDENER_EXTENSION)) { - try (final BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream(), StandardCharsets.UTF_8))) { - AccessWidenerTransformationService.LOGGER.debug("Reading access widener {} from {}", name, resource); - this.reader.read(reader); - } catch (final IOException ex) { - AccessWidenerTransformationService.LOGGER.error("Failed to load access widener {} from {}", name, resource, ex); - } - } else { - AccessWidenerTransformationService.LOGGER.warn("Offered access widener {} from {} that does not end with expected extension '{}'", - name, resource, AccessWidenerTransformationService.ACCESS_WIDENER_EXTENSION); - } - } - - static class AWTransformer implements ITransformer { - private final AccessWidener widener; - - AWTransformer(final AccessWidener widener) { - this.widener = widener; - } - - @Override - public @NonNull ClassNode transform(final ClassNode input, final ITransformerVotingContext context) { - AccessWidenerTransformationService.LOGGER.debug("Transforming {}", context.getClassName()); - final ClassNode output = new ClassNode(Opcodes.ASM9); - final ClassVisitor visitor = AccessWidenerClassVisitor.createClassVisitor(Opcodes.ASM9, output, this.widener); - input.accept(visitor); - return output; - } - - @Override - public @NonNull TransformerVoteResult castVote(final ITransformerVotingContext context) { - final TransformerVoteResult result = context.getReason().equals(ITransformerActivity.CLASSLOADING_REASON) ? TransformerVoteResult.YES : TransformerVoteResult.NO; - AccessWidenerTransformationService.LOGGER.debug("Voting on {} with reason {}, result: {}", context.getClassName(), context.getReason(), result); - return result; - } - - @Override - public @NonNull Set> targets() { - final Set classNames = this.widener.getTargets(); - final Set> targets = new HashSet<>(classNames.size()); - for (final String clazz : classNames) { - targets.add(Target.targetClass(clazz.replace('.', '/'))); - } - return targets; - } - - @Override - public @NonNull TargetType getTargetType() { - return TargetType.CLASS; - } - } -} diff --git a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/SuperclassChanger.java b/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/SuperclassChanger.java deleted file mode 100644 index c56190963e7..00000000000 --- a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/SuperclassChanger.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.transformers.modlauncher; - -import cpw.mods.modlauncher.api.IEnvironment; -import cpw.mods.modlauncher.api.ITransformationService; -import cpw.mods.modlauncher.api.ITransformer; -import cpw.mods.modlauncher.api.ITransformerActivity; -import cpw.mods.modlauncher.api.ITransformerVotingContext; -import cpw.mods.modlauncher.api.TargetType; -import cpw.mods.modlauncher.api.TransformerVoteResult; -import cpw.mods.modlauncher.api.TypesafeMap; -import joptsimple.OptionSpec; -import joptsimple.OptionSpecBuilder; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.framework.qual.DefaultQualifier; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiFunction; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -@DefaultQualifier(NonNull.class) -public class SuperclassChanger implements ITransformationService { - - public static final String NAME = "superclass_change"; - public static final String SUPER_CLASS_EXTENSION = "superclasschange"; - // Pulled from org.spongepowered.asm.launch.MixinLaunchPluginLegacy as - // when Mixin prepares target class nodes, it requests other ITransformers - // to load changes as necessary. - public static final String MIXIN_PLUGIN_REASON = "mixin"; - public static final Supplier> - INSTANCE = IEnvironment.buildKey("sponge:scc", SuperclassChanger.class); - static final Logger LOGGER = LogManager.getLogger(); - - private @MonotonicNonNull OptionSpec configSpec; - private final Map superclassTargets = new ConcurrentHashMap<>(); - - @NonNull - @Override - public String name() { - return SuperclassChanger.NAME; - } - - @Override - public void onLoad(final IEnvironment env, final Set otherServices) { - env.computePropertyIfAbsent(SuperclassChanger.INSTANCE.get(), k -> this); - } - - @Override - public void arguments(final BiFunction argumentBuilder) { - this.configSpec = argumentBuilder.apply("config", "An SCC file to apply at runtime") - .withRequiredArg() - .ofType(String.class); - } - - @Override - public void argumentValues(final OptionResult option) { - option.values(this.configSpec).forEach(file -> { - SuperclassChanger.LOGGER.debug("Loading SCC file: " + file); - final @Nullable URL resource = SuperclassChanger.class.getClassLoader().getResource(file); - if (resource == null) { - SuperclassChanger.LOGGER.warn("Could not find SCC file: " + file); - return; - } - this.offerResource(resource, "command-line"); - }); - } - - @Override - public void initialize(final IEnvironment environment) { - } - - @Override - public @NonNull List> transformers() { - return Collections.singletonList( - new SuperclassTransformer(new ConcurrentHashMap<>(this.superclassTargets))); - } - - // register additional AWs with the transformation service - public void offerResource(final URL resource, final String name) { - if (resource.getFile().endsWith(SuperclassChanger.SUPER_CLASS_EXTENSION)) { - try { - SuperclassChanger.LOGGER.debug("Reading superclass change {} from {}", name, resource); - this.superclassTargets.putAll(SuperclassChanger.readMap(resource)); - } catch (final Exception ex) { - SuperclassChanger.LOGGER.error("Failed to load superclass change {} from {}", name, resource, ex); - } - } else { - SuperclassChanger.LOGGER.warn( - "Offered superclass change {} from {} that does not end with expected extension '{}'", - name, resource, SuperclassChanger.SUPER_CLASS_EXTENSION - ); - } - } - - private static Map readMap(final URL resource) throws IOException { - final Map targets = new HashMap<>(); - try (final BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream(), StandardCharsets.UTF_8))) { - String line; - while ((line = reader.readLine()) != null) { - line = line.trim(); - if (!line.isEmpty()) { - String[] parts = line.split(":", 2); - if (parts.length != 2) { - throw new IllegalArgumentException("Invalid map entry: " + line); - } - targets.put(parts[0], parts[1]); - } - } - } - return targets; - } - - static class SuperclassTransformer implements ITransformer { - private final ConcurrentHashMap superclassTargets; - - public SuperclassTransformer(final ConcurrentHashMap superclassTargets) { - this.superclassTargets = superclassTargets; - } - - @Override - public @NonNull ClassNode transform(final ClassNode input, final ITransformerVotingContext context) { - final String inputKey = input.name.replace("/", "."); - final String newSuperclass = this.superclassTargets.get(inputKey); - if (newSuperclass == null) { - SuperclassChanger.LOGGER.warn("No superclass change for {}", inputKey); - return input; - } - final String sanitizedSuperClass = newSuperclass.replace('.', '/'); - - input.methods.forEach(m -> SuperclassTransformer.transformMethod(m, input.superName, sanitizedSuperClass)); - input.superName = sanitizedSuperClass; - return input; - } - - private static void transformMethod( - final MethodNode node, final String originalSuperclass, final String superClass - ) { - for (final MethodInsnNode insn : SuperclassTransformer.findSuper(node, originalSuperclass)) { - insn.owner = superClass; - } - } - - private static List findSuper( - final MethodNode method, final String originalSuperClass - ) { - final List nodes = new ArrayList<>(); - for (final AbstractInsnNode node : method.instructions.toArray()) { - if (node.getOpcode() == Opcodes.INVOKESPECIAL && originalSuperClass.equals( - ((MethodInsnNode) node).owner)) { - nodes.add((MethodInsnNode) node); - } - } - return nodes; - } - - @Override - public @NonNull TransformerVoteResult castVote(final ITransformerVotingContext context) { - return switch (context.getReason()) { - case ITransformerActivity.CLASSLOADING_REASON, SuperclassChanger.MIXIN_PLUGIN_REASON -> TransformerVoteResult.YES; - default -> TransformerVoteResult.NO; - }; - } - - @Override - public @NonNull Set> targets() { - return this.superclassTargets - .keySet() - .stream() - .map(s -> s.replace('.', '/')) - .map(Target::targetClass) - .collect(Collectors.toSet()); - } - - @Override - public @NonNull TargetType getTargetType() { - return TargetType.CLASS; - } - } - -} diff --git a/modlauncher-transformers/src/main/resources/META-INF/MANIFEST.MF b/modlauncher-transformers/src/main/resources/META-INF/MANIFEST.MF deleted file mode 100644 index 8e5fb5b07e5..00000000000 --- a/modlauncher-transformers/src/main/resources/META-INF/MANIFEST.MF +++ /dev/null @@ -1 +0,0 @@ -Automatic-Module-Name: sponge.modlauncher_transformers diff --git a/modlauncher-transformers/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService b/modlauncher-transformers/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService deleted file mode 100644 index 7b38f18930e..00000000000 --- a/modlauncher-transformers/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService +++ /dev/null @@ -1,2 +0,0 @@ -org.spongepowered.transformers.modlauncher.SuperclassChanger -org.spongepowered.transformers.modlauncher.AccessWidenerTransformationService \ No newline at end of file diff --git a/modlauncher-transformers/src/main/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService b/modlauncher-transformers/src/main/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService deleted file mode 100644 index f95865eabf0..00000000000 --- a/modlauncher-transformers/src/main/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService +++ /dev/null @@ -1 +0,0 @@ -org.spongepowered.transformers.modlauncher.jacoco.JacocoPluginService diff --git a/neoforge/build.gradle.kts b/neoforge/build.gradle.kts index 08f51d8f888..00a599e7780 100644 --- a/neoforge/build.gradle.kts +++ b/neoforge/build.gradle.kts @@ -1,6 +1,8 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import net.neoforged.moddevgradle.internal.RunGameTask +import net.neoforged.nfrtgradle.NeoFormRuntimeTask import org.spongepowered.gradle.impl.AWToAT +import org.spongepowered.gradle.impl.DigestUtil buildscript { repositories { @@ -23,7 +25,6 @@ plugins { val commonProject = parent!! val bootstrapProject = commonProject.project(":bootstrap") -val transformersProject = commonProject.project(":modlauncher-transformers") val libraryManagerProject = commonProject.project(":library-manager") val testPluginsProject: Project? = rootProject.subprojects.find { "testplugins" == it.name } @@ -60,12 +61,8 @@ val serviceLayerConfig = configurations.register("serviceLayer") { extendsFrom(bootLayerConfig.get()) extendsFrom(serviceLibrariesConfig.get()) } -val langLayerConfig = configurations.register("langLayer") { - extendsFrom(bootLayerConfig.get()) -} val gameLayerConfig = configurations.register("gameLayer") { extendsFrom(serviceLayerConfig.get()) - extendsFrom(langLayerConfig.get()) extendsFrom(gameLibrariesConfig.get()) } @@ -92,13 +89,6 @@ val appLaunch by sourceSets.register("applaunch") { } } -// Lang layer -val lang by sourceSets.register("lang") { - configurations.named(implementationConfigurationName) { - extendsFrom(langLayerConfig.get()) - } -} - // Game layer val accessors by sourceSets.register("accessors") { spongeImpl.addDependencyToImplementation(commonAccessors.get(), this) @@ -137,7 +127,6 @@ val main by sourceSets.named("main") { // The rest of the project because we want everything in the initial classpath spongeImpl.addDependencyToRuntimeOnly(commonMixins.get(), this) spongeImpl.addDependencyToRuntimeOnly(mixins, this) - spongeImpl.addDependencyToRuntimeOnly(lang, this) // The bootstrap spongeImpl.addDependencyToRuntimeOnly(bootstrapMain.get(), this) @@ -167,18 +156,17 @@ neoForge { runs { configureEach { // jvmArgument("-Dsponge.bootstrap.debug=true") // Uncomment to debug bootstrap classpath - mainClass = "org.spongepowered.bootstrap.neoforge.NeoForgeBootstrap" - - programArguments.addAll(mixinConfigs.flatMap { sequenceOf("--mixin.config", it) }) } create("client") { client() + mainClass = "org.spongepowered.bootstrap.neoforge.DevClient" } create("server") { server() programArgument("--nogui") + mainClass = "org.spongepowered.bootstrap.neoforge.DevServer" } } } @@ -189,9 +177,7 @@ configurations.testRuntimeOnly.get().extendsFrom(configurations.getByName("modDe dependencies { val service = serviceLibrariesConfig.name service(apiLibs.pluginSpi) - service(project(transformersProject.path)) { - exclude(group = "cpw.mods", module = "modlauncher") - } + service(libs.accessWidener) service(project(libraryManagerProject.path)) val game = gameLibrariesConfig.name @@ -202,7 +188,6 @@ dependencies { game(libs.mixinextras.neo) val serviceShadedLibraries = serviceShadedLibrariesConfig.name - serviceShadedLibraries(project(transformersProject.path)) { isTransitive = false } serviceShadedLibraries(project(libraryManagerProject.path)) { isTransitive = false } val gameShadedLibraries = gameShadedLibrariesConfig.name @@ -221,10 +206,7 @@ dependencies { testImplementation(apiLibs.junit.launcher) testRuntimeOnly(apiLibs.junit.engine) - testImplementation(libs.mockito.core) - testImplementation(libs.mockito.junitJupiter) { - exclude(group = "org.junit.jupiter", module = "junit-jupiter-api") - } + testImplementation(apiLibs.mockito) testRuntimeOnly(libs.jacoco.core) { exclude(group = "org.ow2.asm") @@ -246,18 +228,27 @@ afterEvaluate { val neoManifest = java.manifest { attributes( - "Specification-Title" to "SpongeNeo", - "Specification-Vendor" to "SpongePowered", - "Specification-Version" to apiVersion, - "Implementation-Title" to project.name, - "Implementation-Version" to version, - "Implementation-Vendor" to "SpongePowered" + "Specification-Title" to "SpongeNeo", + "Specification-Vendor" to "SpongePowered", + "Specification-Version" to apiVersion, + "Implementation-Title" to project.name, + "Implementation-Version" to version, + "Implementation-Vendor" to "SpongePowered" ) // These two are included by most CI's System.getenv()["GIT_COMMIT"]?.apply { attributes("Git-Commit" to this) } System.getenv()["GIT_BRANCH"]?.apply { attributes("Git-Branch" to this) } } +val modManifest = java.manifest { + attributes( + "Access-Widener" to "common.accesswidener", + "Superclass-Transformer" to "common.superclasschange,neoforge.superclasschange", + "MixinConfigs" to mixinConfigs.joinToString(","), + "LithiumOptions" to "mixin.world.tick_scheduler=false,mixin.collections.entity_filtering=false" + ) +} + sourceSets { main { blossom.resources { @@ -286,15 +277,13 @@ tasks { standardInput = System.`in` } - jar { - manifest.from(neoManifest) + // NFRT tasks default to a Java 21 launcher; reuse the project toolchain so no extra JDK must be installed + withType(NeoFormRuntimeTask::class).configureEach { + javaExecutable.set(project.javaToolchains.launcherFor(project.java.toolchain).map { it.executablePath.asFile.absolutePath }) } - val langJar by existing(Jar::class) { - manifest.attributes( - "Automatic-Module-Name" to "spongeneo.lang", - "FMLModType" to "LIBRARY" - ) + jar { + manifest.from(neoManifest) } val installerResources = project.layout.buildDirectory.dir("generated/resources/installer") @@ -312,6 +301,13 @@ tasks { dependsOn(emitDependencies) } + named(main.processResourcesTaskName, ProcessResources::class) { + val manifestFile = this.destinationDir.resolve("META-INF/MANIFEST.MF") + doLast { + modManifest.writeTo(manifestFile) + } + } + val servicesShadowJar by register("servicesShadowJar", ShadowJar::class) { group = "build" archiveClassifier.set("services") @@ -346,11 +342,7 @@ tasks { manifest { from(neoManifest) - attributes( - "Access-Widener" to "common.accesswidener", - "Superclass-Transformer" to "common.superclasschange,neoforge.superclasschange", - "MixinConfigs" to mixinConfigs.joinToString(",") - ) + from(modManifest) } from(commonMain.map { it.output }) @@ -372,9 +364,10 @@ tasks { into("jars") { from(shadowJar) rename("spongeneo-(.*)-mod.jar", "spongeneo-mod.jar") + } - from(langJar) - rename("spongeneo-(.*)-lang.jar", "spongeneo-lang.jar") + doLast { + DigestUtil.generateChecksums(archiveFile.get().asFile, "jars", "SHA-256") } } @@ -416,7 +409,7 @@ tasks { jacocoTestReport { sourceSets(commonAppLaunchConf.get(), commonAppLaunch.get(), commonAccessors.get(), commonMixins.get(), commonMain.get()) - sourceSets(appLaunch, lang, accessors, mixins, main) + sourceSets(appLaunch, accessors, mixins, main) dependsOn(test) } } @@ -429,9 +422,6 @@ publishing { artifact(tasks["jar"]) artifact(tasks["sourcesJar"]) - artifact(tasks["langJar"]) - artifact(tasks["langSourcesJar"]) - artifact(tasks["mixinsJar"]) artifact(tasks["mixinsSourcesJar"]) diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties index 61e71ea0a75..33f2bf10d5e 100644 --- a/neoforge/gradle.properties +++ b/neoforge/gradle.properties @@ -2,5 +2,5 @@ name=SpongeNeo implementation=NeoForge description=The SpongeAPI implementation for NeoForge -neoForgeVersion=21.1.211 +neoForgeVersion=26.2.0.62 mixinConfigs=mixins.spongeneo.accessors.json,mixins.spongeneo.api.json,mixins.spongeneo.inventory.json,mixins.spongeneo.core.json,mixins.spongeneo.tracker.json diff --git a/neoforge/src/accessors/java/org/spongepowered/neoforge/accessor/neoforge/registries/RegistrySnapshotAccessor_Neo.java b/neoforge/src/accessors/java/org/spongepowered/neoforge/accessor/neoforge/registries/RegistrySnapshotAccessor_Neo.java index a3c22d65967..4b6cddb1cb7 100644 --- a/neoforge/src/accessors/java/org/spongepowered/neoforge/accessor/neoforge/registries/RegistrySnapshotAccessor_Neo.java +++ b/neoforge/src/accessors/java/org/spongepowered/neoforge/accessor/neoforge/registries/RegistrySnapshotAccessor_Neo.java @@ -25,7 +25,7 @@ package org.spongepowered.neoforge.accessor.neoforge.registries; import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.neoforged.neoforge.registries.RegistrySnapshot; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @@ -34,5 +34,5 @@ public interface RegistrySnapshotAccessor_Neo { @Accessor("ids") - Int2ObjectSortedMap accessor$ids(); + Int2ObjectSortedMap accessor$ids(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/inventory/StonecutterMenuAccessor.java b/neoforge/src/accessors/java/org/spongepowered/neoforge/accessor/world/level/block/entity/AbstractFurnaceBlockEntityAccessor_Neo.java similarity index 75% rename from src/accessors/java/org/spongepowered/common/accessor/world/inventory/StonecutterMenuAccessor.java rename to neoforge/src/accessors/java/org/spongepowered/neoforge/accessor/world/level/block/entity/AbstractFurnaceBlockEntityAccessor_Neo.java index 7b6b94afb21..64bf9c092dc 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/inventory/StonecutterMenuAccessor.java +++ b/neoforge/src/accessors/java/org/spongepowered/neoforge/accessor/world/level/block/entity/AbstractFurnaceBlockEntityAccessor_Neo.java @@ -22,20 +22,21 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.inventory; +package org.spongepowered.neoforge.accessor.world.level.block.entity; -import net.minecraft.world.Container; -import net.minecraft.world.inventory.StonecutterMenu; -import net.minecraft.world.item.crafting.SingleRecipeInput; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.common.UntransformedInvokerError; -@Mixin(StonecutterMenu.class) -public interface StonecutterMenuAccessor { +@Mixin(AbstractFurnaceBlockEntity.class) +public interface AbstractFurnaceBlockEntityAccessor_Neo { - @Invoker("createRecipeInput") static SingleRecipeInput invoker$createRecipeInput(Container $$0) { + @Invoker("canBurn") + static boolean invoker$canBurn(final NonNullList items, final int maxStackSize, final ItemStack burnResult) { throw new UntransformedInvokerError(); } - } diff --git a/neoforge/src/accessors/resources/mixins.spongeneo.accessors.json b/neoforge/src/accessors/resources/mixins.spongeneo.accessors.json index 92a1833be67..206c76d1fec 100644 --- a/neoforge/src/accessors/resources/mixins.spongeneo.accessors.json +++ b/neoforge/src/accessors/resources/mixins.spongeneo.accessors.json @@ -1,8 +1,9 @@ { - "required": true, - "parent": "mixins.sponge.parent.json", - "package": "org.spongepowered.neoforge.accessor", - "mixins": [ - "neoforge.registries.RegistrySnapshotAccessor_Neo" - ] + "required": true, + "parent": "mixins.sponge.parent.json", + "package": "org.spongepowered.neoforge.accessor", + "mixins": [ + "neoforge.registries.RegistrySnapshotAccessor_Neo", + "world.level.block.entity.AbstractFurnaceBlockEntityAccessor_Neo" + ] } \ No newline at end of file diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/SpongeNeoModLocator.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/SpongeNeoModLocator.java deleted file mode 100644 index 6b9920bb342..00000000000 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/SpongeNeoModLocator.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.neoforge.applaunch.loading.moddiscovery; - -import com.google.common.collect.ImmutableMap; -import cpw.mods.jarhandling.JarContents; -import cpw.mods.jarhandling.SecureJar; -import cpw.mods.modlauncher.Environment; -import cpw.mods.modlauncher.Launcher; -import net.neoforged.fml.loading.FMLEnvironment; -import net.neoforged.fml.loading.moddiscovery.readers.JarModsDotTomlModFileReader; -import net.neoforged.neoforgespi.ILaunchContext; -import net.neoforged.neoforgespi.locating.IDiscoveryPipeline; -import net.neoforged.neoforgespi.locating.IModFile; -import net.neoforged.neoforgespi.locating.IModFileCandidateLocator; -import net.neoforged.neoforgespi.locating.IncompatibleFileReporting; -import net.neoforged.neoforgespi.locating.ModFileDiscoveryAttributes; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.spongepowered.neoforge.applaunch.plugin.NeoForgePluginPlatform; - -import java.io.File; -import java.net.URI; -import java.net.URL; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Locale; -import java.util.Map; -import java.util.stream.Stream; - -public final class SpongeNeoModLocator implements IModFileCandidateLocator { - private static final Logger LOGGER = LogManager.getLogger(); - - public SpongeNeoModLocator() { - final Environment env = Launcher.INSTANCE.environment(); - NeoForgePluginPlatform.bootstrap(env); - } - - @Override - public void findCandidates(ILaunchContext context, IDiscoveryPipeline pipeline) { - final ModFileDiscoveryAttributes attributes = ModFileDiscoveryAttributes.DEFAULT.withLocator(this); - - if (!FMLEnvironment.production) { - final String resourcesProp = System.getProperty("sponge.resources"); - if (resourcesProp != null) { - for (final String entry : resourcesProp.split(File.pathSeparator)) { - if (entry.isBlank()) { - continue; - } - - final JarContents jarContents = JarContents.of(Stream.of(entry.split("&")).map(Path::of).toList()); - IModFile modFile = null; - try { - // attempt to load as mod or plugin - modFile = pipeline.readModFile(jarContents, attributes); - } catch (Exception ignored) {} - if (modFile == null) { - // fallback to game library - modFile = IModFile.create(SecureJar.from(jarContents), JarModsDotTomlModFileReader::manifestParser, IModFile.Type.GAMELIBRARY, attributes); - } - pipeline.addModFile(modFile); - } - } - return; - } - - try { - URL rootJar = SpongeNeoModLocator.class.getProtectionDomain().getCodeSource().getLocation(); - FileSystem fs = FileSystems.getFileSystem(rootJar.toURI()); // FML has already opened a file system for this jar - try (Stream st = Files.list(fs.getPath("jars"))) { - st.filter(path -> path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".jar")) - .map(path -> { - try { - URI jij = new URI("jij:" + path.toAbsolutePath().toUri().getRawSchemeSpecificPart()).normalize(); - final Map env = ImmutableMap.of("packagePath", path); - FileSystem jijFS = FileSystems.newFileSystem(jij, env); - return jijFS.getPath("/"); // root of the archive to load - } catch (Exception e) { - throw new RuntimeException(e); - } - }).forEach(path -> pipeline.addPath(path, attributes, IncompatibleFileReporting.WARN_ALWAYS)); - } - } catch (Exception e) { - LOGGER.error("Failed to scan mod candidates", e); - } - } - - @Override - public String toString() { - return "spongeneo"; - } -} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/locator/EnvironmentPluginLocator.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/locator/EnvironmentPluginLocator.java deleted file mode 100644 index 764bcb25fe0..00000000000 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/locator/EnvironmentPluginLocator.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.neoforge.applaunch.loading.moddiscovery.locator; - -import net.neoforged.neoforgespi.ILaunchContext; -import net.neoforged.neoforgespi.locating.IDiscoveryPipeline; -import net.neoforged.neoforgespi.locating.IModFileCandidateLocator; -import net.neoforged.neoforgespi.locating.IncompatibleFileReporting; -import net.neoforged.neoforgespi.locating.ModFileDiscoveryAttributes; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Stream; - -public final class EnvironmentPluginLocator implements IModFileCandidateLocator { - - @Override - public void findCandidates(final ILaunchContext context, final IDiscoveryPipeline pipeline) { - final ModFileDiscoveryAttributes attributes = ModFileDiscoveryAttributes.DEFAULT.withLocator(this); - for (final Path[] paths : EnvironmentPluginLocator.getPluginsPaths()) { - pipeline.addPath(Arrays.asList(paths), attributes, IncompatibleFileReporting.WARN_ON_KNOWN_INCOMPATIBILITY); - } - } - - @Override - public String toString() { - return "environment plugin"; - } - - private static List getPluginsPaths() { - final String env = System.getenv("SPONGE_PLUGINS"); - if (env == null) { - return Collections.emptyList(); - } - - List plugins = new ArrayList<>(); - for (final String entry : env.split(";")) { - if (entry.isBlank()) { - continue; - } - plugins.add(Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new)); - } - - return plugins; - } -} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/reader/PluginFileParser.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/reader/PluginFileParser.java deleted file mode 100644 index 5c17d96749d..00000000000 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/reader/PluginFileParser.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.neoforge.applaunch.loading.moddiscovery.reader; - -import cpw.mods.jarhandling.JarContents; -import cpw.mods.jarhandling.SecureJar; -import net.neoforged.fml.loading.moddiscovery.ModFile; -import net.neoforged.fml.loading.moddiscovery.ModFileInfo; -import net.neoforged.fml.loading.moddiscovery.ModJarMetadata; -import net.neoforged.neoforgespi.language.IModFileInfo; -import net.neoforged.neoforgespi.locating.IModFile; -import net.neoforged.neoforgespi.locating.InvalidModFileException; -import net.neoforged.neoforgespi.locating.ModFileDiscoveryAttributes; -import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.common.applaunch.metadata.PluginMetadataFixer; -import org.spongepowered.common.applaunch.plugin.PluginPlatformConstants; -import org.spongepowered.neoforge.applaunch.loading.metadata.PluginFileConfigurable; -import org.spongepowered.plugin.metadata.builtin.MetadataContainer; -import org.spongepowered.plugin.metadata.builtin.MetadataParser; - -import java.io.Reader; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Optional; - -public final class PluginFileParser { - - @SuppressWarnings("UnstableApiUsage") - private static IModFileInfo parsePluginMetadata(final IModFile modFile) throws InvalidModFileException { - final Path metadataFile = modFile.findResource(PluginPlatformConstants.METADATA_FILE_LOCATION); - - try { - final MetadataContainer container; - try (final Reader reader = Files.newBufferedReader(metadataFile, StandardCharsets.UTF_8)) { - container = MetadataParser.read(reader); - } - - final PluginFileConfigurable config = new PluginFileConfigurable(PluginMetadataFixer.fixPluginIds(container)); - return new ModFileInfo((ModFile) modFile, config, (info) -> {}, List.of()); - } catch (final Exception e) { - AppLaunch.logger().warn("Could not read metadata for plugin file '{}'", modFile, e); - throw new InvalidModFileException("Could not read plugin metadata", modFile.getModFileInfo()); - } - } - - public static IModFile newPluginInstance(final JarContents contents, final ModFileDiscoveryAttributes attributes) { - AppLaunch.logger().debug("Considering plugin file candidate {}", contents.getPrimaryPath()); - - final Optional metadataFile = contents.findFile(PluginPlatformConstants.METADATA_FILE_LOCATION); - if (metadataFile.isEmpty()) { - AppLaunch.logger().debug("Plugin file '{}' is missing a 'sponge_plugins.json' metadata file in META-INF", contents.getPrimaryPath()); - return null; - } - - final ModJarMetadata mjm = new ModJarMetadata(contents); - final IModFile modFile = IModFile.create(SecureJar.from(contents, mjm), PluginFileParser::parsePluginMetadata, attributes); - mjm.setModFile(modFile); - return modFile; - } - - private PluginFileParser() { - } -} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/PluginFileReader.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/PluginFileReader.java new file mode 100644 index 00000000000..31d329aaceb --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/PluginFileReader.java @@ -0,0 +1,74 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.mod.discovery; + +import net.neoforged.fml.jarcontents.JarContents; +import net.neoforged.fml.loading.moddiscovery.ModFile; +import net.neoforged.fml.loading.moddiscovery.ModFileInfo; +import net.neoforged.fml.loading.moddiscovery.ModJarMetadata; +import net.neoforged.neoforgespi.language.IModFileInfo; +import net.neoforged.neoforgespi.locating.IModFile; +import net.neoforged.neoforgespi.locating.IModFileReader; +import net.neoforged.neoforgespi.locating.InvalidModFileException; +import net.neoforged.neoforgespi.locating.ModFileDiscoveryAttributes; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.common.applaunch.AppLaunch; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.neoforge.applaunch.mod.metadata.PluginFileConfigurable; +import org.spongepowered.neoforge.applaunch.plugin.discovery.JarContentsPluginResource; +import org.spongepowered.plugin.discovery.PluginResource; + +import java.util.List; + +@SuppressWarnings("UnstableApiUsage") +public class PluginFileReader implements IModFileReader { + @Override + public @Nullable IModFile read(final JarContents jar, final ModFileDiscoveryAttributes attributes) { + final PluginResource resource = new JarContentsPluginResource(jar); + final PluginDiscovery.Candidate candidate = AppLaunch.pluginPlatform().discovery().candidate(resource); + candidate.readMetadata(); + if (!candidate.pluginFound()) { + return null; + } + return PluginFileReader.newPluginFile(jar, candidate, attributes); + } + + @Override + public String toString() { + return "sponge manifest"; + } + + static IModFile newPluginFile(final JarContents jar, final PluginDiscovery.Candidate candidate, final ModFileDiscoveryAttributes attributes) { + final PluginFileConfigurable config = new PluginFileConfigurable(candidate.resource(), candidate.metadata()); + final ModJarMetadata mjm = new ModJarMetadata(); + final IModFile modFile = new ModFile(jar, mjm, file -> newModFileInfo(file, config), attributes); + mjm.setModFile(modFile); + return modFile; + } + + private static IModFileInfo newModFileInfo(final IModFile file, final PluginFileConfigurable config) throws InvalidModFileException { + return new ModFileInfo((ModFile) file, config, (info) -> {}, List.of()); + } +} diff --git a/neoforge/src/lang/java/org/spongepowered/neoforge/lang/provider/JavaPluginLanguageProvider.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/SpongeLanguageLoader.java similarity index 57% rename from neoforge/src/lang/java/org/spongepowered/neoforge/lang/provider/JavaPluginLanguageProvider.java rename to neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/SpongeLanguageLoader.java index be4d14fcfe3..6da38598c34 100644 --- a/neoforge/src/lang/java/org/spongepowered/neoforge/lang/provider/JavaPluginLanguageProvider.java +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/SpongeLanguageLoader.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge.lang.provider; +package org.spongepowered.neoforge.applaunch.mod.discovery; import net.neoforged.fml.Logging; import net.neoforged.fml.ModContainer; @@ -33,52 +33,38 @@ import net.neoforged.neoforgespi.language.ModFileScanData; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.objectweb.asm.Type; +import org.spongepowered.common.applaunch.AppLaunch; +import org.spongepowered.neoforge.applaunch.plugin.NeoPluginPlatform; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.NoSuchElementException; -public final class JavaPluginLanguageProvider extends BuiltInLanguageLoader { +public final class SpongeLanguageLoader extends BuiltInLanguageLoader { private static final Logger LOGGER = LogManager.getLogger(); - private static final Type PLUGIN_ANNOTATION = Type.getType("Lorg/spongepowered/plugin/builtin/jvm/Plugin;"); - @Override - public String name() { - return "java_plain"; + public SpongeLanguageLoader() { + AppLaunch.pluginPlatform().discovery().registerMixinContainers(); } @Override - public ModContainer loadMod(final IModInfo info, final ModFileScanData modFileScanResults, final ModuleLayer layer) { - final var modClasses = modFileScanResults.getAnnotations().stream() - .filter(ad -> ad.annotationType().equals(JavaPluginLanguageProvider.PLUGIN_ANNOTATION)) - .peek(ad -> LOGGER.debug(Logging.SCAN, "Found @Plugin class {} with id {}", ad.clazz().getClassName(), ad.annotationData().get("value"))) - .filter(data -> JavaPluginLanguageProvider.fixPluginId((String) data.annotationData().get("value")).equals(info.getModId())) - .map(ad -> ad.clazz().getClassName()) - .toList(); - - if (modClasses.size() != 1) { - throw new ModLoadingException(ModLoadingIssue.error("Found {0} plugin entrypoints", modClasses.size())); - } - return JavaPluginLanguageProvider.newPluginModContainer(info, modClasses.get(0), modFileScanResults, layer); + public String name() { + return "sponge"; } - private static ModContainer newPluginModContainer(final IModInfo info, final String className, final ModFileScanData modFileScanData, final ModuleLayer gameLayer) { + @Override + public ModContainer loadMod(final IModInfo info, final ModFileScanData scanData, final ModuleLayer layer) { try { - final Module module = gameLayer.findModule("spongeneo").orElseThrow(() -> new NoSuchElementException("Module spongeneo")); + final Module module = layer.findModule("spongeneo").orElseThrow(() -> new NoSuchElementException("Module spongeneo")); final Class containerClass = Class.forName(module, "org.spongepowered.neoforge.launch.plugin.PluginModContainer"); - final Constructor constructor = containerClass.getConstructor(IModInfo.class, String.class, ModFileScanData.class, ModuleLayer.class); - return (ModContainer) constructor.newInstance(info, className, modFileScanData, gameLayer); + final Constructor constructor = containerClass.getConstructor(IModInfo.class, ModFileScanData.class, ModuleLayer.class); + return (ModContainer) constructor.newInstance(info, scanData, layer); } catch (final InvocationTargetException e) { - LOGGER.fatal(Logging.LOADING, "Failed to build plugin", e); + SpongeLanguageLoader.LOGGER.fatal(Logging.LOADING, "Failed to build plugin", e); throw new ModLoadingException(ModLoadingIssue.error("Failed to build plugin")); } catch (final Exception e) { - LOGGER.fatal(Logging.LOADING, "Unable to load PluginModContainer", e); + SpongeLanguageLoader.LOGGER.fatal(Logging.LOADING, "Unable to load PluginModContainer", e); throw new ModLoadingException(ModLoadingIssue.error("Unable to load PluginModContainer")); } } - - private static String fixPluginId(final String id) { - return id.replace('-', '_'); - } } diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/SpongeNeoDependencyLocator.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/SpongeNeoDependencyLocator.java similarity index 83% rename from neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/SpongeNeoDependencyLocator.java rename to neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/SpongeNeoDependencyLocator.java index e4e0293c216..af1a969c1a1 100644 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/SpongeNeoDependencyLocator.java +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/SpongeNeoDependencyLocator.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge.applaunch.loading.moddiscovery; +package org.spongepowered.neoforge.applaunch.mod.discovery; -import cpw.mods.jarhandling.SecureJar; +import net.neoforged.fml.jarcontents.JarContents; import net.neoforged.fml.loading.FMLEnvironment; import net.neoforged.fml.loading.moddiscovery.readers.JarModsDotTomlModFileReader; import net.neoforged.neoforgespi.locating.IDependencyLocator; @@ -36,8 +36,9 @@ import org.spongepowered.common.applaunch.AppLaunch; import org.spongepowered.common.applaunch.plugin.PluginPlatform; import org.spongepowered.libs.LibraryManager; -import org.spongepowered.neoforge.applaunch.loading.moddiscovery.library.Log4JLogger; +import org.spongepowered.neoforge.applaunch.mod.discovery.library.Log4JLogger; +import java.io.IOException; import java.nio.file.Path; import java.util.List; @@ -46,7 +47,7 @@ public class SpongeNeoDependencyLocator implements IDependencyLocator { @Override public void scanMods(final List loadedMods, final IDiscoveryPipeline pipeline) { - if (!FMLEnvironment.production) { + if (!FMLEnvironment.isProduction()) { return; } @@ -70,7 +71,14 @@ public void scanMods(final List loadedMods, final IDiscoveryPipeline p final Path path = library.file(); SpongeNeoDependencyLocator.LOGGER.debug("Proposing jar {} as a game library", path); - pipeline.addModFile(IModFile.create(SecureJar.from(path), JarModsDotTomlModFileReader::manifestParser, IModFile.Type.GAMELIBRARY, attributes)); + final JarContents jar; + try { + jar = JarContents.ofPath(path); + } catch (IOException e) { + throw new RuntimeException("Failed to create jar from " + path, e); + } + + pipeline.addModFile(IModFile.create(jar, JarModsDotTomlModFileReader::manifestParser, IModFile.Type.GAMELIBRARY, attributes)); } } diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/SpongeNeoModLocator.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/SpongeNeoModLocator.java new file mode 100644 index 00000000000..0aec3618e38 --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/SpongeNeoModLocator.java @@ -0,0 +1,106 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.mod.discovery; + +import net.neoforged.fml.jarcontents.JarContents; +import net.neoforged.fml.loading.moddiscovery.readers.JarModsDotTomlModFileReader; +import net.neoforged.neoforgespi.ILaunchContext; +import net.neoforged.neoforgespi.locating.IDiscoveryPipeline; +import net.neoforged.neoforgespi.locating.IModFile; +import net.neoforged.neoforgespi.locating.IModFileCandidateLocator; +import net.neoforged.neoforgespi.locating.ModFileDiscoveryAttributes; +import org.spongepowered.common.applaunch.AppLaunch; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.neoforge.applaunch.plugin.NeoPluginPlatform; +import org.spongepowered.neoforge.applaunch.plugin.discovery.JarContentsPluginResource; +import org.spongepowered.neoforge.applaunch.plugin.discovery.NeoPluginDiscovery; + +import java.nio.file.Path; + +public final class SpongeNeoModLocator implements IModFileCandidateLocator { + private final NeoPluginDiscovery discovery; + + public SpongeNeoModLocator() { + this.discovery = AppLaunch.bootstrap(NeoPluginPlatform::new).discovery(); + this.discovery.discoverPluginResources(); + } + + @Override + public void findCandidates(ILaunchContext context, IDiscoveryPipeline pipeline) { + final ModFileDiscoveryAttributes attributes = ModFileDiscoveryAttributes.DEFAULT.withLocator(this); + int virtualJarCount = 0; + + for (final PluginDiscovery.Candidate candidate : this.discovery.candidates()) { + if (candidate.resource() instanceof JarContentsPluginResource resource) { + // skip duplicates + if (resource.paths().stream().anyMatch(context::isLocated)) { + candidate.setModFound(); + candidate.logResult(); + continue; + } + + // attempt to load as mod or plugin + IModFile modFile = null; + try { + modFile = pipeline.readModFile(resource.jar(), attributes); + } catch (Exception ignored) {} + + if (modFile == null) { + // fallback + if (!candidate.gameResource()) { + candidate.logResult(); + continue; + } + modFile = IModFile.create(resource.jar(), JarModsDotTomlModFileReader::manifestParser, IModFile.Type.GAMELIBRARY, attributes); + } else if (!candidate.pluginFound()) { + candidate.setModFound(); + } + + resource.paths().forEach(context::addLocated); + pipeline.addModFile(modFile); + } else { + candidate.readMetadata(); + if (!candidate.pluginFound()) { + candidate.logResult(); + continue; + } + + // This jar is only used as an identifier and does not need to actually exist + final JarContents jar = JarContents.empty(Path.of("sponge_resource_" + virtualJarCount++)); + + pipeline.addModFile(PluginFileReader.newPluginFile(jar, candidate, attributes)); + } + + candidate.logResult(); + } + + this.discovery.logMetadataWarnings(); + } + + @Override + public String toString() { + return "spongeneo"; + } +} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/library/Log4JLogger.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/library/Log4JLogger.java similarity index 96% rename from neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/library/Log4JLogger.java rename to neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/library/Log4JLogger.java index efac9b34303..0d5c7c61cc7 100644 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/library/Log4JLogger.java +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/discovery/library/Log4JLogger.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge.applaunch.loading.moddiscovery.library; +package org.spongepowered.neoforge.applaunch.mod.discovery.library; import org.spongepowered.libs.Logger; diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginConflictConfigurable.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginConflictConfigurable.java new file mode 100644 index 00000000000..c7c9c18c618 --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginConflictConfigurable.java @@ -0,0 +1,70 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.mod.metadata; + +import net.neoforged.neoforgespi.language.IConfigurable; +import net.neoforged.neoforgespi.language.IModInfo; +import org.spongepowered.common.applaunch.plugin.PluginPlatformConstants; +import org.spongepowered.plugin.metadata.model.PluginConflict; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +// ModVersion +public final class PluginConflictConfigurable implements IConfigurable { + + private final PluginConflict conflict; + + public PluginConflictConfigurable(final PluginConflict conflict) { + this.conflict = conflict; + } + + @SuppressWarnings("unchecked") + @Override + public Optional getConfigElement(final String... key) { + if (key.length != 1) { + return Optional.empty(); + } + + return switch (key[0]) { + case "modId" -> (Optional) Optional.of(this.conflict.id()); + case "type" -> (Optional) Optional.of(PluginConflictConfigurable.fatalToDependencyType(this.conflict.fatal()).toString()); + case "versionRange" -> (Optional) Optional.of(this.conflict.version().toString()); + case "reason" -> (Optional) this.conflict.reason(); + case "side" -> (Optional) Optional.of(IModInfo.DependencySide.BOTH.toString()); + default -> Optional.empty(); + }; + } + + @Override + public List getConfigList(final String... key) { + return Collections.emptyList(); + } + + private static IModInfo.DependencyType fatalToDependencyType(final boolean fatal) { + return fatal && !PluginPlatformConstants.IGNORE_FATAL_CONFLICTS ? IModInfo.DependencyType.INCOMPATIBLE : IModInfo.DependencyType.DISCOURAGED; + } +} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/metadata/PluginDependencyConfigurable.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginDependencyConfigurable.java similarity index 78% rename from neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/metadata/PluginDependencyConfigurable.java rename to neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginDependencyConfigurable.java index 9f0996dce7e..2e678c525df 100644 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/metadata/PluginDependencyConfigurable.java +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginDependencyConfigurable.java @@ -22,11 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge.applaunch.loading.metadata; +package org.spongepowered.neoforge.applaunch.mod.metadata; import net.neoforged.neoforgespi.language.IConfigurable; import net.neoforged.neoforgespi.language.IModInfo; -import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.plugin.metadata.model.PluginDependency; import java.util.Collections; @@ -36,11 +35,9 @@ // ModVersion public final class PluginDependencyConfigurable implements IConfigurable { - private final PluginMetadata metadata; private final PluginDependency dependency; - public PluginDependencyConfigurable(final PluginMetadata metadata, final PluginDependency dependency) { - this.metadata = metadata; + public PluginDependencyConfigurable(final PluginDependency dependency) { this.dependency = dependency; } @@ -53,9 +50,9 @@ public Optional getConfigElement(final String... key) { return switch (key[0]) { case "modId" -> (Optional) Optional.of(this.dependency.id()); - case "type" -> (Optional) Optional.of(this.optionalToDependencyType(this.dependency.optional()).toString()); + case "type" -> (Optional) Optional.of(PluginDependencyConfigurable.optionalToDependencyType(this.dependency.optional()).toString()); case "versionRange" -> (Optional) Optional.of(this.dependency.version().toString()); - case "ordering" -> (Optional) Optional.of(this.loadToOrdering(this.dependency.loadOrder()).toString()); + case "ordering" -> (Optional) Optional.of(PluginDependencyConfigurable.loadToOrdering(this.dependency.loadOrder()).toString()); case "side" -> (Optional) Optional.of(IModInfo.DependencySide.BOTH.toString()); default -> Optional.empty(); }; @@ -66,7 +63,7 @@ public List getConfigList(final String... key) { return Collections.emptyList(); } - private IModInfo.Ordering loadToOrdering(final PluginDependency.LoadOrder order) { + private static IModInfo.Ordering loadToOrdering(final PluginDependency.LoadOrder order) { return switch (order) { case UNDEFINED -> IModInfo.Ordering.NONE; case BEFORE -> IModInfo.Ordering.BEFORE; @@ -74,7 +71,7 @@ private IModInfo.Ordering loadToOrdering(final PluginDependency.LoadOrder order) }; } - private IModInfo.DependencyType optionalToDependencyType(final boolean optional) { + private static IModInfo.DependencyType optionalToDependencyType(final boolean optional) { return optional ? IModInfo.DependencyType.OPTIONAL : IModInfo.DependencyType.REQUIRED; } } diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/metadata/PluginFileConfigurable.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginFileConfigurable.java similarity index 60% rename from neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/metadata/PluginFileConfigurable.java rename to neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginFileConfigurable.java index 5a97ac24ed4..ad785d27012 100644 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/metadata/PluginFileConfigurable.java +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginFileConfigurable.java @@ -22,26 +22,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge.applaunch.loading.metadata; +package org.spongepowered.neoforge.applaunch.mod.metadata; import net.neoforged.neoforgespi.language.IConfigurable; +import org.spongepowered.plugin.discovery.PluginResource; import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.MetadataContainer; -import org.spongepowered.plugin.metadata.model.PluginDependency; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; // ModFileInfo public final class PluginFileConfigurable implements IConfigurable { + private final Map plugins = new LinkedHashMap<>(); + private final PluginResource resource; - private final MetadataContainer container; - - public PluginFileConfigurable(final MetadataContainer container) { - this.container = container; + public PluginFileConfigurable(final PluginResource resource, final Collection plugins) { + for (final PluginMetadata plugin : plugins) { + this.plugins.put(plugin.id(), plugin); + } + this.resource = resource; } @SuppressWarnings("unchecked") @@ -56,27 +56,40 @@ public Optional getConfigElement(final String... key) { return Optional.empty(); } - if ("modLoader".equals(query)) { - return (Optional) Optional.of(this.container.loader().name()); + if ("spongeResource".equals(query)) { + return Optional.of((T) this.resource); } - if ("loaderVersion".equals(query)) { - return (Optional) Optional.of(this.container.loader().version().toString()); + if ("modLoader".equals(query)) { + return Optional.of((T) "sponge"); } if ("license".equals(query)) { - return (Optional) Optional.of(this.container.license()); + final String joinedLicenses = this.plugins.values().stream() + .flatMap(m -> m.license().stream()).distinct().collect(Collectors.joining(", ")); + return Optional.of((T) (joinedLicenses.isEmpty() ? "?" : joinedLicenses)); + } + + if ("lithium:options".equals(query)) { + return this.resource.property("LithiumOptions").map(value -> { + final Map map = new HashMap<>(); + for (final String entry : value.split(",")) { + final int i = entry.indexOf('='); + map.put(entry.substring(0, i), Boolean.valueOf(entry.substring(i + 1))); + } + return (T) map; + }); } if (key.length == 2) { final String plugin = key[1]; - final PluginMetadata metadata = this.container.metadata(plugin).orElse(null); + final PluginMetadata metadata = this.plugins.get(plugin); if (metadata == null) { return Optional.empty(); } if ("modproperties".equals(query)) { - return (Optional) Optional.of(metadata.properties()); + return Optional.of((T) metadata.properties()); } } @@ -95,38 +108,24 @@ public List getConfigList(final String... key) { } if ("mods".equals(query)) { - final Set metadataById = this.container.metadata(); - if (metadataById.isEmpty()) { - return Collections.emptyList(); - } - - final List metadataConfigurables = new ArrayList<>(); - metadataById.forEach((metadata) -> metadataConfigurables.add(new PluginMetadataConfigurable(metadata))); - return metadataConfigurables; + return this.plugins.values().stream().map(PluginMetadataConfigurable::new).toList(); } if (key.length != 2) { return Collections.emptyList(); } - final String plugin = key[1]; - final PluginMetadata metadata = this.container.metadata(plugin).orElse(null); + final String pluginId = key[1]; + final PluginMetadata metadata = this.plugins.get(pluginId); if (metadata == null) { return Collections.emptyList(); } if ("dependencies".equals(query)) { - final Set dependencies = metadata.dependencies(); - if (dependencies.isEmpty()) { - return Collections.emptyList(); - } - - final List depConfigurables = new ArrayList<>(); - for (final PluginDependency dependency : dependencies) { - depConfigurables.add(new PluginDependencyConfigurable(metadata, dependency)); - } - - return depConfigurables; + return Stream.concat( + metadata.dependencies().stream().map(PluginDependencyConfigurable::new), + metadata.conflicts().stream().map(PluginConflictConfigurable::new) + ).toList(); } return Collections.emptyList(); @@ -136,7 +135,6 @@ private int requiredConfigElements(final String query) { if ("dependencies".equals(query) || "modproperties".equals(query)) { return 2; } - return 1; } } diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/metadata/PluginMetadataConfigurable.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginMetadataConfigurable.java similarity index 88% rename from neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/metadata/PluginMetadataConfigurable.java rename to neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginMetadataConfigurable.java index 9baa86ce681..02964711caa 100644 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/metadata/PluginMetadataConfigurable.java +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/mod/metadata/PluginMetadataConfigurable.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge.applaunch.loading.metadata; +package org.spongepowered.neoforge.applaunch.mod.metadata; import net.neoforged.neoforgespi.language.IConfigurable; import org.spongepowered.plugin.metadata.PluginMetadata; @@ -32,13 +32,7 @@ import java.util.Optional; // ModInfo -public final class PluginMetadataConfigurable implements IConfigurable { - - private final PluginMetadata metadata; - - public PluginMetadataConfigurable(final PluginMetadata metadata) { - this.metadata = metadata; - } +public record PluginMetadataConfigurable(PluginMetadata metadata) implements IConfigurable { @SuppressWarnings("unchecked") @Override @@ -53,6 +47,7 @@ public Optional getConfigElement(final String... key) { case "displayName" -> (Optional) this.metadata.name(); case "description" -> (Optional) this.metadata.description(); case "displayTest" -> Optional.of((T) "IGNORE_SERVER_VERSION"); + case "spongeMetadata" -> Optional.of((T) this.metadata); default -> Optional.empty(); }; } diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/NeoForgePluginPlatform.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/NeoForgePluginPlatform.java deleted file mode 100644 index 5e780d757f8..00000000000 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/NeoForgePluginPlatform.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.neoforge.applaunch.plugin; - -import cpw.mods.modlauncher.Environment; -import cpw.mods.modlauncher.api.IEnvironment; -import net.neoforged.fml.loading.FMLPaths; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.common.applaunch.config.LaunchConfig; -import org.spongepowered.common.applaunch.config.TokenReplacement; -import org.spongepowered.common.applaunch.plugin.PluginPlatform; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; - -public final class NeoForgePluginPlatform implements PluginPlatform { - - private static volatile boolean bootstrapped; - - private final Environment environment; - private final Logger logger; - private final LaunchConfig config; - private final TokenReplacement tokens; - private final List pluginDirectories; - - public static synchronized void bootstrap(final Environment environment) { - if (NeoForgePluginPlatform.bootstrapped) { - return; - } - NeoForgePluginPlatform.bootstrapped = true; - final NeoForgePluginPlatform platform; - try { - platform = new NeoForgePluginPlatform(environment); - } catch (final IOException e) { - throw new RuntimeException(e); - } - AppLaunch.setPluginPlatform(platform); - } - - private NeoForgePluginPlatform(final Environment environment) throws IOException { - this.environment = environment; - this.logger = LogManager.getLogger("plugin"); - this.config = LaunchConfig.load(this.baseDirectory(), true); - - this.tokens = new TokenReplacement(); - this.tokens.register("BASE_DIR", this.baseDirectory()); - this.tokens.register("CONFIG_DIR", this.configDirectory()); - this.tokens.register("MODS_DIR", FMLPaths.MODSDIR.get()); - - final Path additionalPluginsDirectory = Path.of(this.tokens.replace(this.config.additionalPluginsDirectory())); - Files.createDirectories(additionalPluginsDirectory); - this.pluginDirectories = List.of(FMLPaths.MODSDIR.get(), additionalPluginsDirectory); - } - - @Override - public String version() { - return this.environment.getProperty(IEnvironment.Keys.VERSION.get()).orElse("dev"); - } - - @Override - public Logger logger() { - return this.logger; - } - - @Override - public boolean vanilla() { - return false; - } - - @Override - public Path baseDirectory() { - return FMLPaths.GAMEDIR.get(); - } - - @Override - public Path configDirectory() { - return FMLPaths.CONFIGDIR.get(); - } - - @Override - public LaunchConfig config() { - return this.config; - } - - @Override - public TokenReplacement tokens() { - return this.tokens; - } - - @Override - public List pluginDirectories() { - return this.pluginDirectories; - } -} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/NeoPluginPlatform.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/NeoPluginPlatform.java new file mode 100644 index 00000000000..e391b071b94 --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/NeoPluginPlatform.java @@ -0,0 +1,77 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.plugin; + +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.fml.loading.FMLPaths; +import org.spongepowered.common.applaunch.plugin.PluginPlatform; +import org.spongepowered.neoforge.applaunch.plugin.discovery.JarContentsPluginResource; +import org.spongepowered.neoforge.applaunch.plugin.discovery.NeoPluginDiscovery; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; + +import java.nio.file.Path; + +public final class NeoPluginPlatform extends PluginPlatform { + private final NeoPluginDiscovery discovery; + + public NeoPluginPlatform() { + this.discovery = new NeoPluginDiscovery(this.environment()); + } + + @Override + public boolean vanilla() { + return false; + } + + @Override + public Path baseDirectory() { + return FMLPaths.GAMEDIR.get(); + } + + @Override + public Path configDirectory() { + return FMLPaths.CONFIGDIR.get(); + } + + @Override + public Path modsDirectory() { + return FMLPaths.MODSDIR.get(); + } + + @Override + public NeoPluginDiscovery discovery() { + return this.discovery; + } + + @Override + public JVMPluginResource create(final Path[] paths) { + return new JarContentsPluginResource(paths); + } + + @Override + public void addLoaderCloseCallback(final AutoCloseable closeable) { + FMLLoader.getCurrent().addCloseCallback(closeable); + } +} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/JarContentsPluginResource.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/JarContentsPluginResource.java new file mode 100644 index 00000000000..aa8af788d5d --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/JarContentsPluginResource.java @@ -0,0 +1,123 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.plugin.discovery; + +import net.neoforged.fml.jarcontents.JarContents; +import net.neoforged.fml.jarmoduleinfo.JarModuleInfo; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.spongepowered.common.applaunch.plugin.discovery.SpongeJVMPluginResource; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.lang.module.ModuleDescriptor; +import java.net.URI; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.jar.Manifest; + +public final class JarContentsPluginResource implements SpongeJVMPluginResource { + private final List paths; + private final JarContents jar; + private @MonotonicNonNull ModuleDescriptor descriptor; + + public JarContentsPluginResource(final Path[] paths) { + Objects.requireNonNull(paths, "paths"); + if (paths.length == 0) { + throw new IllegalArgumentException("Need at least one path"); + } + this.paths = List.of(paths); + try { + this.jar = JarContents.ofPaths(this.paths); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public JarContentsPluginResource(final JarContents jar) { + Objects.requireNonNull(jar, "jar"); + this.paths = List.copyOf(jar.getContentRoots()); + this.jar = jar; + } + + public JarContents jar() { + return this.jar; + } + + @Override + public List paths() { + return this.paths; + } + + @Override + public Optional locate(final String path) { + return this.jar.findFile(Objects.requireNonNull(path, "path")); + } + + @Override + public Optional open(final String path) { + try { + return Optional.ofNullable(this.jar.openFile(Objects.requireNonNull(path, "path"))); + } catch (final IOException ignored) { + return Optional.empty(); + } + } + + @Override + public Manifest manifest() { + return this.jar.getManifest(); + } + + @Override + public ModuleDescriptor module() { + if (this.descriptor == null) { + this.descriptor = JarModuleInfo.from(this.jar).createDescriptor(this.jar); + } + return this.descriptor; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof JarContentsPluginResource that)) { + return false; + } + return this.paths.equals(that.paths); + } + + @Override + public int hashCode() { + return this.paths.hashCode(); + } + + @Override + public String toString() { + return "JarContentsPluginResource[paths=" + this.paths + "]"; + } +} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/NeoPluginDiscovery.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/NeoPluginDiscovery.java new file mode 100644 index 00000000000..32cc856a141 --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/NeoPluginDiscovery.java @@ -0,0 +1,86 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.plugin.discovery; + +import net.neoforged.fml.jarcontents.CompositeJarContents; +import net.neoforged.fml.jarcontents.JarContents; +import net.neoforged.fml.loading.mixin.FMLMixinService; +import org.spongepowered.asm.service.MixinService; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.common.applaunch.plugin.discovery.SpongeJVMPluginResource; +import org.spongepowered.plugin.Environment; + +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +public class NeoPluginDiscovery extends PluginDiscovery { + private ClassLoader currentLoader; + + public NeoPluginDiscovery(Environment environment) { + super(environment); + this.currentLoader = getClass().getClassLoader(); + } + + @Override + protected ServiceLoader newServiceLoader(final Class serviceClass) { + return ServiceLoader.load(serviceClass, this.currentLoader); + } + + @SuppressWarnings("UnstableApiUsage") + @Override + protected void appendDiscoveryServices(final List resources, final int batch) throws Exception { + final ClassLoader parentLoader = this.currentLoader; + + // See FMLLoader#appendLoader + final List rootUrls = new ArrayList<>(resources.size()); + for (final SpongeJVMPluginResource resource : resources) { + final JarContents jar = ((JarContentsPluginResource) resource).jar(); + if (jar instanceof final CompositeJarContents compositeJar && compositeJar.isFiltered()) { + throw new IllegalArgumentException("Cannot use simple URLClassLoader for filtered content " + jar); + } + for (final Path contentRoot : jar.getContentRoots()) { + rootUrls.add(contentRoot.toUri().toURL()); + } + } + + final ClassLoader loader = new URLClassLoader("SPONGE-DISCOVERY-BATCH-" + batch, rootUrls.toArray(URL[]::new), parentLoader); + this.currentLoader = loader; + this.environment.logger().debug("Built new service layer {} on top of {}.", loader.getName(), parentLoader.getName()); + } + + @SuppressWarnings("UnstableApiUsage") + public void registerMixinContainers() { + final FMLMixinService mixinService = (FMLMixinService) MixinService.getService(); + for (final PluginDiscovery.Candidate candidate : this.candidates()) { + if (candidate.pluginFound()) { + mixinService.addMixinContainer(new PluginResourceContainerHandle(candidate.metadata().getFirst().id(), candidate.resource())); + } + } + } +} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/PluginResourceContainerHandle.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/PluginResourceContainerHandle.java new file mode 100644 index 00000000000..9740c4aa951 --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/PluginResourceContainerHandle.java @@ -0,0 +1,54 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.plugin.discovery; + +import org.spongepowered.asm.launch.platform.container.IContainerHandle; +import org.spongepowered.plugin.discovery.PluginResource; + +import java.util.Collection; +import java.util.List; + +public record PluginResourceContainerHandle(String id, PluginResource resource) implements IContainerHandle { + + @Override + public String getAttribute(final String name) { + return this.resource.property(name).orElse(null); + } + + @Override + public Collection getNestedContainers() { + return List.of(); + } + + @Override + public String getId() { + return this.id; + } + + @Override + public String getDescription() { + return this.toString(); + } +} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/SpongeNeoResourceLocator.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/SpongeNeoResourceLocator.java new file mode 100644 index 00000000000..25d7167d4af --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/discovery/SpongeNeoResourceLocator.java @@ -0,0 +1,83 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.plugin.discovery; + +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.fml.loading.FMLPaths; +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.discovery.UnknownResourceStrategy; + +import java.net.URI; +import java.net.URL; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +public final class SpongeNeoResourceLocator implements PluginResourceLocator { + + @Override + public String name() { + return "spongeneo"; + } + + @Override + public Collection locatePluginResources(final Environment environment) throws Exception { + final List resources = new LinkedList<>(); + if (!FMLEnvironment.isProduction()) { + return resources; + } + + // Official JiJ cannot be used in a service jar. We have to reimplement it. + // FML 10 only supports files directly on disk, so we must extract them. + final URL rootJar = SpongeNeoResourceLocator.class.getProtectionDomain().getCodeSource().getLocation(); + final Path cacheDir = FMLPaths.JIJ_CACHEDIR.get(); + + try (final FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + rootJar), Map.of())) { + final Path jarsDir = fs.getPath("jars"); + + // Difference with the official JiJ: our checksums are precalculated, to avoid unnecessary IO. + for (final String line : Files.readAllLines(jarsDir.resolve("checksums.txt"))) { + final int i = line.indexOf(' '); + final String digest = line.substring(0, i), name = line.substring(i + 1); + final Path target = cacheDir.resolve(digest).resolve(name); + + // FML does not verify the cached file checksum, so neither do we. + // The checksum is only there to distinguish different files. + if (!Files.exists(target)) { + Files.createDirectories(target.getParent()); + Files.copy(jarsDir.resolve(name), target); + } + + resources.add(new Result(JVMPluginResource.create(environment, target), UnknownResourceStrategy.WARN)); + } + } + + return resources; + } +} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/metadata/PluginMetadataConverter.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/metadata/PluginMetadataConverter.java new file mode 100644 index 00000000000..4000325613f --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/plugin/metadata/PluginMetadataConverter.java @@ -0,0 +1,119 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.plugin.metadata; + +import net.neoforged.fml.loading.moddiscovery.ModInfo; +import net.neoforged.neoforgespi.language.IModFileInfo; +import net.neoforged.neoforgespi.language.IModInfo; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.metadata.builtin.InheritableMetadata; +import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; +import org.spongepowered.plugin.metadata.model.PluginConflict; +import org.spongepowered.plugin.metadata.model.PluginContributor; +import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginLinks; +import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; + +import java.net.URISyntaxException; +import java.net.URL; +import java.util.List; +import java.util.Optional; + +public final class PluginMetadataConverter { + private static final VersionRange ANY_VERSION; + + static { + try { + ANY_VERSION = VersionRange.createFromVersionSpec("*"); + } catch (InvalidVersionSpecificationException e) { + throw new RuntimeException(e); + } + } + + public static PluginMetadata modToPlugin(final IModInfo info) { + final List loaders = info.getOwningFile().requiredLanguageLoaders(); + final PluginLoaderSpecification loaderSpec; + if (loaders.isEmpty()) { + loaderSpec = new PluginLoaderSpecification("neoforge", PluginMetadataConverter.ANY_VERSION); + } else { + final VersionRange loaderVersion = loaders.getFirst().acceptedVersions(); + loaderSpec = new PluginLoaderSpecification("neoforge:" + loaders.getFirst().languageName(), loaderVersion == null ? PluginMetadataConverter.ANY_VERSION : loaderVersion); + } + + final InheritableMetadata.Builder builder = InheritableMetadata.builder(); + builder + .name(info.getDisplayName()) + .version(info.getVersion()) + .loader(loaderSpec) + .description(info.getDescription()) + .properties(info.getModProperties()); + + if (info instanceof ModInfo modInfo) { + builder.addContributor(new PluginContributor(modInfo.getConfigElement("authors").orElse("unknown").toString(), Optional.empty())); + try { + final URL issueURL = modInfo.getOwningFile().getIssueURL(); + builder.links(new PluginLinks(null, null, issueURL == null ? null : issueURL.toURI())); + } catch (URISyntaxException ignored) {} + } + + for (final IModInfo.ModVersion dependency : info.getDependencies()) { + switch (dependency.getType()) { + case REQUIRED: + case OPTIONAL: + builder.addDependency(new PluginDependency( + dependency.getModId(), + dependency.getVersionRange(), + PluginMetadataConverter.orderingToLoad(dependency.getOrdering()), + dependency.getType() == IModInfo.DependencyType.OPTIONAL + )); + break; + case INCOMPATIBLE: + case DISCOURAGED: + builder.addConflict(new PluginConflict( + dependency.getModId(), + dependency.getVersionRange(), + dependency.getType() == IModInfo.DependencyType.INCOMPATIBLE, + dependency.getReason() + )); + break; + } + } + + return StandardPluginMetadata.builder() + .id(info.getModId()) + .override(builder.build()) + .build(); + } + + private static PluginDependency.LoadOrder orderingToLoad(final IModInfo.Ordering ordering) { + return switch (ordering) { + case BEFORE -> PluginDependency.LoadOrder.BEFORE; + case AFTER -> PluginDependency.LoadOrder.AFTER; + case NONE -> PluginDependency.LoadOrder.UNDEFINED; + }; + } +} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/NeoAccessWidenerTransformer.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/NeoAccessWidenerTransformer.java new file mode 100644 index 00000000000..c9e40cb4a85 --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/NeoAccessWidenerTransformer.java @@ -0,0 +1,87 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.transformation; + +import net.neoforged.fml.jarcontents.JarContents; +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.fml.loading.moddiscovery.ModFileInfo; +import net.neoforged.neoforgespi.transformation.ClassProcessor; +import net.neoforged.neoforgespi.transformation.ProcessorName; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.common.applaunch.transformation.AccessWidenerTransformer; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.jar.Attributes; + +public class NeoAccessWidenerTransformer extends AccessWidenerTransformer implements ClassProcessor { + private static final ProcessorName NAME = new ProcessorName("neoforge", AccessWidenerTransformer.NAME); + + @Override + public ProcessorName name() { + return NeoAccessWidenerTransformer.NAME; + } + + @Override + public boolean handlesClass(final SelectionContext context) { + return this.targetClasses().contains(context.type().getClassName()); + } + + @Override + public ComputeFlags processClass(final TransformationContext context) { + final ClassNode input = context.node(); + final ClassNode output = this.transform(input); + + input.access = output.access; + input.permittedSubclasses = output.permittedSubclasses; + input.innerClasses = output.innerClasses; + input.fields = output.fields; + input.methods = output.methods; + + return ComputeFlags.SIMPLE_REWRITE; + } + + @Override + protected Collection collectResources() { + final Collection resources = new ArrayList<>(); + for (ModFileInfo fileInfo : FMLLoader.getCurrent().getLoadingModList().getModFiles()) { + final JarContents jar = fileInfo.getFile().getContents(); + final Attributes attributes = jar.getManifest().getMainAttributes(); + + final String attribute = attributes.getValue(AccessWidenerTransformer.MANIFEST_ATTRIBUTE); + if (attribute != null) { + for (final String path : attribute.split(",")) { + try { + resources.add(jar.findFile(path).get().toURL()); + } catch (final Exception e) { + LOGGER.warn("Failed to locate access widener {} from {}", path, fileInfo.getFile().getFileName(), e); + } + } + } + } + return resources; + } +} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/NeoListenerTransformer.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/NeoListenerTransformer.java new file mode 100644 index 00000000000..b2943095b43 --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/NeoListenerTransformer.java @@ -0,0 +1,75 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.transformation; + +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.fml.loading.moddiscovery.ModFileInfo; +import net.neoforged.neoforgespi.language.ModFileScanData; +import net.neoforged.neoforgespi.transformation.ClassProcessor; +import net.neoforged.neoforgespi.transformation.ProcessorName; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.objectweb.asm.Type; +import org.spongepowered.common.applaunch.transformation.ListenerTransformer; + +import java.util.HashSet; +import java.util.Set; + +public class NeoListenerTransformer extends ListenerTransformer implements ClassProcessor { + private static final ProcessorName NAME = new ProcessorName("neoforge", ListenerTransformer.NAME); + + private @MonotonicNonNull Set targets; + + private synchronized void loadTargets() { + if (this.targets == null) { + this.targets = new HashSet<>(); + + final Type listenerType = Type.getType(ListenerTransformer.ANNOTATION_DESC); + for (ModFileInfo fileInfo : FMLLoader.getCurrent().getLoadingModList().getModFiles()) { + for (ModFileScanData.AnnotationData annotation : fileInfo.getFile().getScanResult().getAnnotations()) { + if (listenerType.equals(annotation.annotationType())) { + this.targets.add(annotation.clazz()); + } + } + } + } + } + + @Override + public ProcessorName name() { + return NeoListenerTransformer.NAME; + } + + @Override + public boolean handlesClass(final SelectionContext context) { + this.loadTargets(); + return this.targets.contains(context.type()); + } + + @Override + public ComputeFlags processClass(final TransformationContext context) { + this.transform(context.node()); + return ComputeFlags.COMPUTE_FRAMES; + } +} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/NeoSuperclassChangeTransformer.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/NeoSuperclassChangeTransformer.java new file mode 100644 index 00000000000..012ec22ad78 --- /dev/null +++ b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/NeoSuperclassChangeTransformer.java @@ -0,0 +1,78 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.applaunch.transformation; + +import net.neoforged.fml.jarcontents.JarContents; +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.fml.loading.moddiscovery.ModFileInfo; +import net.neoforged.neoforgespi.transformation.ClassProcessor; +import net.neoforged.neoforgespi.transformation.ProcessorName; +import org.spongepowered.common.applaunch.transformation.SuperclassChangeTransformer; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.jar.Attributes; + +public class NeoSuperclassChangeTransformer extends SuperclassChangeTransformer implements ClassProcessor { + private static final ProcessorName NAME = new ProcessorName("neoforge", SuperclassChangeTransformer.NAME); + + @Override + public ProcessorName name() { + return NeoSuperclassChangeTransformer.NAME; + } + + @Override + public boolean handlesClass(final SelectionContext context) { + return this.targetClasses().contains(context.type().getClassName()); + } + + @Override + public ComputeFlags processClass(final TransformationContext context) { + this.transform(context.node()); + return ComputeFlags.SIMPLE_REWRITE; + } + + @Override + protected Collection collectResources() { + final Collection resources = new ArrayList<>(); + for (ModFileInfo fileInfo : FMLLoader.getCurrent().getLoadingModList().getModFiles()) { + final JarContents jar = fileInfo.getFile().getContents(); + final Attributes attributes = jar.getManifest().getMainAttributes(); + + final String attribute = attributes.getValue(SuperclassChangeTransformer.MANIFEST_ATTRIBUTE); + if (attribute != null) { + for (final String path : attribute.split(",")) { + try { + resources.add(jar.findFile(path).get().toURL()); + } catch (final Exception e) { + LOGGER.warn("Failed to locate superclass changer {} from {}", path, fileInfo.getFile().getFileName(), e); + } + } + } + } + return resources; + } +} diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/SpongeNeoTransformationService.java b/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/SpongeNeoTransformationService.java deleted file mode 100644 index d63a361cbe3..00000000000 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/SpongeNeoTransformationService.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.neoforge.applaunch.transformation; - -import cpw.mods.jarhandling.SecureJar; -import cpw.mods.modlauncher.Environment; -import cpw.mods.modlauncher.Launcher; -import cpw.mods.modlauncher.api.IEnvironment; -import cpw.mods.modlauncher.api.IModuleLayerManager; -import cpw.mods.modlauncher.api.ITransformationService; -import cpw.mods.modlauncher.api.ITransformer; -import net.neoforged.fml.loading.LoadingModList; -import net.neoforged.fml.loading.moddiscovery.ModFileInfo; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.transformers.modlauncher.AccessWidenerTransformationService; -import org.spongepowered.transformers.modlauncher.SuperclassChanger; - -import java.net.MalformedURLException; -import java.util.List; -import java.util.Set; -import java.util.jar.Attributes; - -public class SpongeNeoTransformationService implements ITransformationService { - private static final Logger LOGGER = LogManager.getLogger(); - - @NonNull - @Override - public String name() { - return "spongeneo"; - } - - @Override - public void initialize(final IEnvironment environment) { - } - - @Override - public void onLoad(final IEnvironment env, final Set otherServices) { - } - - @Override - public List completeScan(IModuleLayerManager layerManager) { - final Environment env = Launcher.INSTANCE.environment(); - final AccessWidenerTransformationService accessWidener = env.getProperty(AccessWidenerTransformationService.INSTANCE.get()).orElse(null); - final SuperclassChanger superclassChanger = env.getProperty(SuperclassChanger.INSTANCE.get()).orElse(null); - - if (accessWidener != null || superclassChanger != null) { - for (ModFileInfo fileInfo : LoadingModList.get().getModFiles()) { - final SecureJar jar = fileInfo.getFile().getSecureJar(); - final Attributes attributes = jar.moduleDataProvider().getManifest().getMainAttributes(); - - if (accessWidener != null) { - final String awPaths = attributes.getValue("Access-Widener"); - if (awPaths != null) { - final String jarFileName = fileInfo.getFile().getFileName(); - LOGGER.debug("Registering access wideners from {} ...", jarFileName); - - for (final String awPath : awPaths.split(",")) { - try { - accessWidener.offerResource(jar.getPath(awPath).toUri().toURL(), awPath); - } catch (final MalformedURLException e) { - LOGGER.warn("Failed to register access widener {} from {}", awPath, jarFileName, e); - } - } - } - } - - if (superclassChanger != null) { - final String scPaths = attributes.getValue("Superclass-Transformer"); - if (scPaths != null) { - final String jarFileName = fileInfo.getFile().getFileName(); - LOGGER.debug("Registering superclass changers from {} ...", jarFileName); - - for (final String scPath : scPaths.split(",")) { - try { - superclassChanger.offerResource(jar.getPath(scPath).toUri().toURL(), scPath); - } catch (final MalformedURLException e) { - LOGGER.warn("Failed to register superclass changer {} from {}", scPath, jarFileName, e); - } - } - } - } - } - } - - return List.of(); - } - - @NonNull - @Override - public List> transformers() { - return List.of(new ListenerTransformer()); - } -} diff --git a/neoforge/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService b/neoforge/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService deleted file mode 100644 index 6561a0860dd..00000000000 --- a/neoforge/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService +++ /dev/null @@ -1 +0,0 @@ -org.spongepowered.neoforge.applaunch.transformation.SpongeNeoTransformationService diff --git a/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.language.IModLanguageLoader b/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.language.IModLanguageLoader new file mode 100644 index 00000000000..226127c74e5 --- /dev/null +++ b/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.language.IModLanguageLoader @@ -0,0 +1 @@ +org.spongepowered.neoforge.applaunch.mod.discovery.SpongeLanguageLoader diff --git a/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IDependencyLocator b/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IDependencyLocator index 386ebb032d7..33c4820c8c1 100644 --- a/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IDependencyLocator +++ b/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IDependencyLocator @@ -1 +1 @@ -org.spongepowered.neoforge.applaunch.loading.moddiscovery.SpongeNeoDependencyLocator \ No newline at end of file +org.spongepowered.neoforge.applaunch.mod.discovery.SpongeNeoDependencyLocator \ No newline at end of file diff --git a/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileCandidateLocator b/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileCandidateLocator index c92ba9dda26..9f21f501e4f 100644 --- a/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileCandidateLocator +++ b/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileCandidateLocator @@ -1,3 +1 @@ -org.spongepowered.neoforge.applaunch.loading.moddiscovery.SpongeNeoModLocator -org.spongepowered.neoforge.applaunch.loading.moddiscovery.locator.EnvironmentPluginLocator -org.spongepowered.neoforge.applaunch.loading.moddiscovery.locator.PluginsDirectoryLocator \ No newline at end of file +org.spongepowered.neoforge.applaunch.mod.discovery.SpongeNeoModLocator diff --git a/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileReader b/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileReader index 58be04367a6..2770e5f95dc 100644 --- a/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileReader +++ b/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileReader @@ -1 +1 @@ -org.spongepowered.neoforge.applaunch.loading.moddiscovery.reader.PluginFileReader \ No newline at end of file +org.spongepowered.neoforge.applaunch.mod.discovery.PluginFileReader \ No newline at end of file diff --git a/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.transformation.ClassProcessor b/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.transformation.ClassProcessor new file mode 100644 index 00000000000..de4422cd642 --- /dev/null +++ b/neoforge/src/applaunch/resources/META-INF/services/net.neoforged.neoforgespi.transformation.ClassProcessor @@ -0,0 +1,3 @@ +org.spongepowered.neoforge.applaunch.transformation.NeoAccessWidenerTransformer +org.spongepowered.neoforge.applaunch.transformation.NeoListenerTransformer +org.spongepowered.neoforge.applaunch.transformation.NeoSuperclassChangeTransformer diff --git a/neoforge/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator b/neoforge/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator new file mode 100644 index 00000000000..be7de628e45 --- /dev/null +++ b/neoforge/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator @@ -0,0 +1,2 @@ +org.spongepowered.neoforge.applaunch.plugin.discovery.SpongeNeoResourceLocator +org.spongepowered.common.applaunch.plugin.discovery.SpongePropertyResourceLocator diff --git a/neoforge/src/lang/resources/META-INF/MANIFEST.MF b/neoforge/src/lang/resources/META-INF/MANIFEST.MF deleted file mode 100644 index c93f76cce69..00000000000 --- a/neoforge/src/lang/resources/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Automatic-Module-Name: spongeneo.lang -FMLModType: LIBRARY diff --git a/neoforge/src/lang/resources/META-INF/services/net.neoforged.neoforgespi.language.IModLanguageLoader b/neoforge/src/lang/resources/META-INF/services/net.neoforged.neoforgespi.language.IModLanguageLoader deleted file mode 100644 index 656f03f2f30..00000000000 --- a/neoforge/src/lang/resources/META-INF/services/net.neoforged.neoforgespi.language.IModLanguageLoader +++ /dev/null @@ -1 +0,0 @@ -org.spongepowered.neoforge.lang.provider.JavaPluginLanguageProvider \ No newline at end of file diff --git a/neoforge/src/main/java/org/spongepowered/neoforge/SpongeNeoMod.java b/neoforge/src/main/java/org/spongepowered/neoforge/SpongeNeo.java similarity index 84% rename from neoforge/src/main/java/org/spongepowered/neoforge/SpongeNeoMod.java rename to neoforge/src/main/java/org/spongepowered/neoforge/SpongeNeo.java index b1755d6254d..57197b21027 100644 --- a/neoforge/src/main/java/org/spongepowered/neoforge/SpongeNeoMod.java +++ b/neoforge/src/main/java/org/spongepowered/neoforge/SpongeNeo.java @@ -24,10 +24,10 @@ */ package org.spongepowered.neoforge; +import com.google.inject.Inject; import net.minecraft.client.Minecraft; import net.neoforged.bus.api.IEventBus; import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.common.Mod; import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; import net.neoforged.neoforge.common.NeoForge; @@ -39,27 +39,26 @@ import org.apache.logging.log4j.Logger; import org.spongepowered.api.Client; import org.spongepowered.api.Server; -import org.spongepowered.api.Sponge; import org.spongepowered.common.entity.SpongeEntityTypes; import org.spongepowered.common.entity.living.human.HumanEntity; import org.spongepowered.common.hooks.PlatformHooks; import org.spongepowered.common.launch.Launch; import org.spongepowered.common.launch.Lifecycle; import org.spongepowered.common.launch.config.core.ConfigHandle; -import org.spongepowered.common.network.channel.SpongeChannelManager; -import org.spongepowered.common.network.packet.SpongePacketHandler; import org.spongepowered.neoforge.hook.NeoChannelHooks; import org.spongepowered.neoforge.hook.NeoEventHooks; import org.spongepowered.neoforge.hook.NeoGeneralHooks; import org.spongepowered.neoforge.hook.NeoItemHooks; import org.spongepowered.neoforge.hook.NeoWorldHooks; +import org.spongepowered.plugin.builtin.jvm.Plugin; -@Mod("spongeneo") -public final class SpongeNeoMod { +@Plugin("spongeneo") +public final class SpongeNeo { private final Logger logger = LogManager.getLogger("spongeneo"); - public SpongeNeoMod(IEventBus modBus) { + @Inject + public SpongeNeo(IEventBus modBus) { // modBus: add all FML events with it modBus.addListener(this::onCommonSetup); modBus.addListener(this::onClientSetup); @@ -77,24 +76,12 @@ public SpongeNeoMod(IEventBus modBus) { } private void onCommonSetup(final FMLCommonSetupEvent event) { - final Lifecycle lifecycle = Launch.instance().lifecycle(); - lifecycle.callConstructEvent(); - lifecycle.callRegisterFactoryEvent(); - lifecycle.callRegisterBuilderEvent(); - lifecycle.callRegisterChannelEvent(); - lifecycle.establishGameServices(); - lifecycle.establishDataKeyListeners(); - - SpongePacketHandler.init((SpongeChannelManager) Sponge.channelManager()); - this.logger.info("SpongeNeo v{} initialized", Launch.instance().platformPlugin().metadata().version()); } private void onClientSetup(final FMLClientSetupEvent event) { final Client minecraft = (Client) Minecraft.getInstance(); final Lifecycle lifecycle = Launch.instance().lifecycle(); - lifecycle.establishDataProviders(); - lifecycle.callRegisterDataEvent(); lifecycle.establishClientRegistries(minecraft); lifecycle.callStartingEngineEvent(minecraft); } @@ -105,11 +92,8 @@ public void onServerAboutToStart(final ServerAboutToStartEvent event) { ConfigHandle.setSaveSuppressed(false); final Lifecycle lifecycle = Launch.instance().lifecycle(); - lifecycle.establishServerServices(); - lifecycle.establishServerFeatures(); - lifecycle.establishServerRegistries((Server) event.getServer()); lifecycle.callStartingEngineEvent((Server) event.getServer()); } diff --git a/neoforge/src/main/java/org/spongepowered/neoforge/launch/NeoLaunch.java b/neoforge/src/main/java/org/spongepowered/neoforge/launch/NeoLaunch.java index ae9e0b98419..669b0527584 100644 --- a/neoforge/src/main/java/org/spongepowered/neoforge/launch/NeoLaunch.java +++ b/neoforge/src/main/java/org/spongepowered/neoforge/launch/NeoLaunch.java @@ -30,16 +30,19 @@ import com.google.inject.Module; import com.google.inject.Stage; import net.neoforged.api.distmarker.Dist; -import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.fml.ModList; +import net.neoforged.fml.loading.FMLEnvironment; import org.spongepowered.common.applaunch.plugin.PluginPlatform; import org.spongepowered.common.inject.SpongeCommonModule; import org.spongepowered.common.inject.SpongeModule; import org.spongepowered.common.launch.Launch; +import org.spongepowered.neoforge.launch.inject.ModContainerModule; import org.spongepowered.neoforge.launch.inject.SpongeNeoModule; import org.spongepowered.neoforge.launch.plugin.NeoPluginManager; import org.spongepowered.plugin.PluginContainer; import java.util.List; +import java.util.Optional; public final class NeoLaunch extends Launch { @@ -53,7 +56,7 @@ public NeoLaunch(final PluginPlatform platform) { @Override public boolean dedicatedServer() { - return FMLLoader.getDist() == Dist.DEDICATED_SERVER; + return FMLEnvironment.getDist() == Dist.DEDICATED_SERVER; } @Override @@ -63,7 +66,7 @@ public NeoPluginManager pluginManager() { @Override public Stage injectionStage() { - return FMLLoader.isProduction() ? Stage.PRODUCTION : Stage.DEVELOPMENT; + return FMLEnvironment.isProduction() ? Stage.PRODUCTION : Stage.DEVELOPMENT; } @Override @@ -88,4 +91,9 @@ public PluginContainer platformPlugin() { return this.spongeNeoPlugin; } + + @Override + public Optional modModule(final PluginContainer plugin) { + return ModList.get().getModContainerById(plugin.metadata().id()).map(ModContainerModule::new); + } } diff --git a/neoforge/src/main/java/org/spongepowered/neoforge/launch/inject/ModContainerModule.java b/neoforge/src/main/java/org/spongepowered/neoforge/launch/inject/ModContainerModule.java new file mode 100644 index 00000000000..414e59a7e6d --- /dev/null +++ b/neoforge/src/main/java/org/spongepowered/neoforge/launch/inject/ModContainerModule.java @@ -0,0 +1,43 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.launch.inject; + +import com.google.inject.PrivateModule; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; + +public final class ModContainerModule extends PrivateModule { + private final ModContainer modContainer; + + public ModContainerModule(final ModContainer modContainer) { + this.modContainer = modContainer; + } + + @Override + protected void configure() { + this.bind(ModContainer.class).toInstance(this.modContainer); + this.bind(IEventBus.class).toInstance(this.modContainer.getEventBus()); + } +} diff --git a/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/NeoPluginContainer.java b/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/NeoPluginContainer.java index e3dd33d8703..1765c27a30a 100644 --- a/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/NeoPluginContainer.java +++ b/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/NeoPluginContainer.java @@ -25,37 +25,44 @@ package org.spongepowered.neoforge.launch.plugin; import com.google.common.collect.MapMaker; -import com.google.inject.Injector; import net.neoforged.fml.ModContainer; -import net.neoforged.fml.loading.moddiscovery.ModInfo; +import net.neoforged.fml.jarcontents.JarContents; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.spongepowered.common.launch.plugin.SpongePluginContainer; +import org.spongepowered.neoforge.applaunch.plugin.discovery.JarContentsPluginResource; +import org.spongepowered.neoforge.applaunch.plugin.metadata.PluginMetadataConverter; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.discovery.PluginResource; import org.spongepowered.plugin.metadata.PluginMetadata; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.Map; -import java.util.Objects; import java.util.Optional; -public class NeoPluginContainer implements SpongePluginContainer { +public class NeoPluginContainer implements PluginContainer { private final ModContainer modContainer; + private final JarContents jar; private Logger logger; private PluginMetadata pluginMetadata; - private Object instance = new Object(); - private Injector injector; + private PluginResource pluginResource; private NeoPluginContainer(final ModContainer modContainer) { this.modContainer = modContainer; + this.jar = modContainer.getModInfo().getOwningFile().getFile().getContents(); + } + + @Override + public PluginResource resource() { + if (this.pluginResource == null) { + this.pluginResource = new JarContentsPluginResource(this.jar); + } + return this.pluginResource; } @Override public PluginMetadata metadata() { if (this.pluginMetadata == null) { - this.pluginMetadata = PluginMetadataConverter.modToPlugin((ModInfo) this.modContainer.getModInfo()); + this.pluginMetadata = PluginMetadataConverter.modToPlugin(this.modContainer.getModInfo()); } return this.pluginMetadata; } @@ -68,33 +75,12 @@ public Logger logger() { return this.logger; } - @Override - public Optional locateResource(String relative) { - final Path p = this.modContainer.getModInfo().getOwningFile().getFile().findResource(Objects.requireNonNull(relative, "relative")); - return Files.exists(p) ? Optional.of(p.toUri()) : Optional.empty(); - } - - @Override - public Object instance() { - return this.instance; - } - - public void setInstance(final Object instance) { - this.instance = instance; - } - - @Override - public Optional injector() { - return Optional.ofNullable(this.injector); - } + private static final Map mods = new MapMaker().weakKeys().makeMap(); - public void setInjector(final Injector injector) { - this.injector = injector; - } - - private static final Map containers = new MapMaker().weakKeys().makeMap(); - - public static NeoPluginContainer of(final ModContainer modContainer) { - return containers.computeIfAbsent(modContainer, NeoPluginContainer::new); + public static Optional of(final ModContainer modContainer) { + if (modContainer instanceof PluginModContainer plugin) { + return plugin.container(); + } + return Optional.of(NeoPluginContainer.mods.computeIfAbsent(modContainer, NeoPluginContainer::new)); } } diff --git a/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/NeoPluginManager.java b/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/NeoPluginManager.java index 538ae3e9f4b..9eb003f1c66 100644 --- a/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/NeoPluginManager.java +++ b/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/NeoPluginManager.java @@ -38,12 +38,13 @@ @Singleton public final class NeoPluginManager implements SpongePluginManager { + @SuppressWarnings("removal") @Override public Optional fromInstance(final Object instance) { for (final ModContainer mod : ModList.get().getSortedMods()) { - final NeoPluginContainer plugin = NeoPluginContainer.of(mod); - if (plugin.instance() == instance) { - return Optional.of(plugin); + final Optional plugin = NeoPluginContainer.of(mod); + if (plugin.isPresent() && plugin.get().instance() == instance) { + return plugin; } } return Optional.empty(); @@ -51,13 +52,13 @@ public Optional fromInstance(final Object instance) { @Override public Optional plugin(final String id) { - return ModList.get().getModContainerById(Objects.requireNonNull(id, "id")).map(NeoPluginContainer::of); + return ModList.get().getModContainerById(Objects.requireNonNull(id, "id")).flatMap(NeoPluginContainer::of); } @Override public Collection plugins() { final ImmutableList.Builder builder = ImmutableList.builder(); - ModList.get().forEachModInOrder(mod -> builder.add(NeoPluginContainer.of(mod))); + ModList.get().forEachModInOrder(mod -> NeoPluginContainer.of(mod).ifPresent(builder::add)); return builder.build(); } diff --git a/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/PluginMetadataConverter.java b/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/PluginMetadataConverter.java deleted file mode 100644 index 4ad12384a0a..00000000000 --- a/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/PluginMetadataConverter.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.neoforge.launch.plugin; - -import net.neoforged.fml.loading.moddiscovery.ModInfo; -import net.neoforged.neoforgespi.language.IModInfo; -import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginContributor; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginDependency; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginLinks; -import org.spongepowered.plugin.metadata.model.PluginDependency; - -import java.util.ArrayList; -import java.util.List; - -public final class PluginMetadataConverter { - - public static PluginMetadata modToPlugin(final ModInfo info) { - final StandardPluginMetadata.Builder builder = StandardPluginMetadata.builder(); - builder - .id(info.getModId()) - .name(info.getDisplayName()) - .version(info.getVersion().toString()) - .description(info.getDescription()) - .entrypoint("unknown") - .addContributor(StandardPluginContributor.builder().name(info.getConfigElement("authors").orElse("unknown").toString()).build()) - .links(StandardPluginLinks.builder().issues(info.getOwningFile().getIssueURL()).build()) - .properties(info.getModProperties()); - - final List dependencies = new ArrayList<>(); - for (final IModInfo.ModVersion dependency : info.getDependencies()) { - final StandardPluginDependency.Builder depBuilder = StandardPluginDependency.builder(); - depBuilder - .id(dependency.getModId()) - .loadOrder(PluginMetadataConverter.orderingToLoad(dependency.getOrdering())) - .version(dependency.getVersionRange().toString()); - - dependencies.add(depBuilder.build()); - } - - builder.dependencies(dependencies); - return builder.build(); - } - - private static PluginDependency.LoadOrder orderingToLoad(final IModInfo.Ordering ordering) { - return switch (ordering) { - case BEFORE -> PluginDependency.LoadOrder.BEFORE; - case AFTER -> PluginDependency.LoadOrder.AFTER; - case NONE -> PluginDependency.LoadOrder.UNDEFINED; - }; - } -} diff --git a/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/PluginModContainer.java b/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/PluginModContainer.java index 4529e2c37b0..dc8886ce029 100644 --- a/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/PluginModContainer.java +++ b/neoforge/src/main/java/org/spongepowered/neoforge/launch/plugin/PluginModContainer.java @@ -24,7 +24,6 @@ */ package org.spongepowered.neoforge.launch.plugin; -import com.google.inject.Injector; import net.neoforged.bus.EventBusErrorMessage; import net.neoforged.bus.api.BusBuilder; import net.neoforged.bus.api.Event; @@ -32,53 +31,48 @@ import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.Logging; import net.neoforged.fml.ModContainer; -import net.neoforged.fml.ModLoadingContext; import net.neoforged.fml.ModLoadingException; import net.neoforged.fml.ModLoadingIssue; import net.neoforged.fml.event.IModBusEvent; import net.neoforged.fml.javafmlmod.AutomaticEventSubscriber; -import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforgespi.language.IModInfo; import net.neoforged.neoforgespi.language.ModFileScanData; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.spongepowered.common.inject.plugin.PluginGuice; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spongepowered.common.launch.Launch; -import org.spongepowered.neoforge.launch.event.NeoEventManager; +import org.spongepowered.common.launch.plugin.loader.PluginCandidate; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; -import java.lang.reflect.InvocationTargetException; +import java.util.Optional; -// Spongified FMLModContainer public final class PluginModContainer extends ModContainer { private static final Logger LOGGER = LogManager.getLogger(); - private final ModFileScanData scanResults; - private final IEventBus eventBus; - private final Class modClass; + private final PluginCandidate candidate; private final Module module; - public PluginModContainer(IModInfo info, String className, ModFileScanData modFileScanResults, ModuleLayer gameLayer) { + private final ModFileScanData scanData; + private final IEventBus eventBus; + + private @MonotonicNonNull PluginContainer container; + + public PluginModContainer(final IModInfo info, final ModFileScanData scanData, final ModuleLayer layer) { super(info); - LOGGER.debug(Logging.LOADING, "Creating PluginModContainer instance for {}", className); - this.scanResults = modFileScanResults; + LOGGER.debug(Logging.LOADING, "Creating PluginModContainer instance for {}", info.getModId()); + this.scanData = scanData; this.eventBus = BusBuilder.builder() .setExceptionHandler(this::onEventFailed) .markerType(IModBusEvent.class) .allowPerPhasePost() .build(); - this.module = gameLayer.findModule(info.getOwningFile().moduleName()).orElseThrow(); - ModLoadingContext context = ModLoadingContext.get(); - try { - context.setActiveContainer(this); - this.modClass = Class.forName(this.module, className); - LOGGER.trace(Logging.LOADING, "Loaded plugin class {} with {}", this.modClass.getName(), this.modClass.getClassLoader()); - } catch (Throwable e) { - LOGGER.error(Logging.LOADING, "Failed to load class {}", className, e); - throw new ModLoadingException(ModLoadingIssue.error("fml.modloadingissue.failedtoloadmodclass").withCause(e).withAffectedMod(info)); - } finally { - context.setActiveContainer(null); - } + final PluginResource resource = info.getOwningFile().getConfig().getConfigElement("spongeResource").get(); + final PluginMetadata metadata = info.getConfig().getConfigElement("spongeMetadata").get(); + this.candidate = Launch.instance().pluginFactory().create(resource, metadata); + this.module = layer.findModule(info.getOwningFile().getFile().getId()).orElseThrow(); } private void onEventFailed(IEventBus iEventBus, Event event, EventListener[] iEventListeners, int i, Throwable throwable) { @@ -88,31 +82,21 @@ private void onEventFailed(IEventBus iEventBus, Event event, EventListener[] iEv @Override protected void constructMod() { try { - LOGGER.trace(Logging.LOADING, "Loading plugin instance {} of type {}", getModId(), this.modClass.getName()); - - final NeoPluginContainer pluginContainer = NeoPluginContainer.of(this); - final Injector pluginInjector = PluginGuice.create(pluginContainer, this.modClass, Launch.instance().lifecycle().platformInjector()); - final Object modInstance = pluginInjector.getInstance(this.modClass); - pluginContainer.setInjector(pluginInjector); - pluginContainer.setInstance(modInstance); - ((NeoEventManager) NeoForge.EVENT_BUS).registerListeners(pluginContainer, modInstance); - - LOGGER.trace(Logging.LOADING, "Loaded plugin instance {} of type {}", getModId(), this.modClass.getName()); - } catch (Throwable e) { - if (e instanceof InvocationTargetException) { - e = e.getCause(); - } - LOGGER.error(Logging.LOADING, "Failed to create plugin instance. PluginID: {}, class {}", getModId(), this.modClass.getName(), e); - throw new ModLoadingException(ModLoadingIssue.error("fml.modloadingissue.failedtoloadmod").withCause(e).withAffectedMod(modInfo)); + LOGGER.trace(Logging.LOADING, "Loading plugin container {}", getModId()); + this.container = this.candidate.load(); + LOGGER.trace(Logging.LOADING, "Loaded plugin container {}", getModId()); + } catch (Exception e) { + LOGGER.error(Logging.LOADING, "Failed to create plugin container {}.", getModId(), e); + throw new ModLoadingException(ModLoadingIssue.error("fml.modloadingissue.failedtoloadmod").withCause(e).withAffectedMod(this.modInfo)); } try { LOGGER.trace(Logging.LOADING, "Injecting Automatic event subscribers for {}", getModId()); - AutomaticEventSubscriber.inject(this, this.scanResults, this.module); + AutomaticEventSubscriber.inject(this, this.scanData, this.module); LOGGER.trace(Logging.LOADING, "Completed Automatic event subscribers for {}", getModId()); } catch (Throwable e) { LOGGER.error(Logging.LOADING, "Failed to register automatic subscribers. ModID: {}", getModId(), e); - throw new ModLoadingException(ModLoadingIssue.error("fml.modloadingissue.failedtoloadmod").withCause(e).withAffectedMod(modInfo)); + throw new ModLoadingException(ModLoadingIssue.error("fml.modloadingissue.failedtoloadmod").withCause(e).withAffectedMod(this.modInfo)); } } @@ -120,4 +104,8 @@ protected void constructMod() { public IEventBus getEventBus() { return this.eventBus; } + + public Optional container() { + return Optional.ofNullable(this.container); + } } diff --git a/neoforge/src/main/resource-templates/META-INF/neoforge.mods.toml b/neoforge/src/main/resource-templates/META-INF/neoforge.mods.toml deleted file mode 100644 index afc5bbedc83..00000000000 --- a/neoforge/src/main/resource-templates/META-INF/neoforge.mods.toml +++ /dev/null @@ -1,62 +0,0 @@ -modLoader="javafml" -loaderVersion="[4,)" -license="MIT License" - -issueTrackerURL="https://github.com/SpongePowered/Sponge/issues" - - -[[mods]] -modId="spongeneo" -version="{{ version }}" -displayName="SpongeNeo" -displayTest="IGNORE_SERVER_VERSION" -credits="SpongePowered and Contributors" -authors="SpongePowered" -description = "{{ description }}" - -[[dependencies.spongeneo]] -modId="neoforge" -type="required" -versionRange="[{{ neoForgeVersion }},)" -ordering="AFTER" - -[[dependencies.spongeneo]] -modId="sponge" -type="required" -versionRange="[{{ version }}]" -ordering="AFTER" - - -[[mods]] -modId="sponge" -version="{{ version }}" -displayName="Sponge" -displayTest="IGNORE_SERVER_VERSION" -credits="SpongePowered and Contributors" -authors="SpongePowered" -description="The SpongeAPI implementation targeting vanilla Minecraft and 3rd party platforms" - -[[dependencies.sponge]] -modId="spongeapi" -type="required" -versionRange="[12.0.0,)" -ordering="AFTER" - - -[[mods]] -modId="spongeapi" -version="12.0.0" -displayName="SpongeAPI" -displayTest="IGNORE_SERVER_VERSION" -credits="SpongePowered and Contributors" -authors="SpongePowered" -description="A Minecraft plugin API" - - -["lithium:options"] -"mixin.world.tick_scheduler"=false -"mixin.collections.entity_filtering"=false - -[[dependencies.spongeneo]] -modId="adventure_platform_neoforge" -type="incompatible" diff --git a/neoforge/src/main/resource-templates/META-INF/sponge_plugins.json b/neoforge/src/main/resource-templates/META-INF/sponge_plugins.json new file mode 100644 index 00000000000..631ae5853bf --- /dev/null +++ b/neoforge/src/main/resource-templates/META-INF/sponge_plugins.json @@ -0,0 +1,65 @@ +{ + "global": { + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "MIT", + "links": { + "homepage": "https://www.spongepowered.org", + "source": "https://www.spongepowered.org/source", + "issues": "https://www.spongepowered.org/issues" + }, + "contributors": [ + { + "name": "SpongePowered", + "description": "Lead Developer" + } + ] + }, + "plugins": [ + { + "id": "spongeneo", + "name": "SpongeNeo", + "version": "{{ version }}", + "entrypoints": ["org.spongepowered.neoforge.SpongeNeo"], + "description": "{{ description }}", + "dependencies": [ + { + "id": "neoforge", + "version": "[{{ neoForgeVersion }},)" + }, + { + "id": "sponge", + "version": "{{ version }}" + } + ], + "conflicts": [ + { + "id": "adventure_platform_neoforge", + "version": "*", + "fatal": true, + "reason": "SpongeNeo provides its own implementation of Adventure" + } + ] + }, + { + "id": "sponge", + "name": "Sponge", + "version": "{{ version }}", + "description": "The common Sponge implementation", + "dependencies": [ + { + "id": "spongeapi", + "version": "{{ apiVersion }}" + } + ] + }, + { + "id": "spongeapi", + "name": "SpongeAPI", + "version": "{{ apiVersion }}", + "description": "The Minecraft API specification" + } + ] +} \ No newline at end of file diff --git a/neoforge/src/main/resources/META-INF/MANIFEST.MF b/neoforge/src/main/resources/META-INF/MANIFEST.MF deleted file mode 100644 index 6818dcc8cd0..00000000000 --- a/neoforge/src/main/resources/META-INF/MANIFEST.MF +++ /dev/null @@ -1 +0,0 @@ -Superclass-Transformer: common.superclasschange,neoforge.superclasschange diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/api/event/block/ChangeBlockEvent_AllMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/api/event/block/ChangeBlockEvent_AllMixin_Neo.java index b3d32099da6..21ba5afdabb 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/api/event/block/ChangeBlockEvent_AllMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/api/event/block/ChangeBlockEvent_AllMixin_Neo.java @@ -28,7 +28,7 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.neoforged.bus.api.Event; -import net.neoforged.neoforge.event.level.BlockEvent; +import net.neoforged.neoforge.event.level.block.BreakBlockEvent; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.block.transaction.BlockTransaction; import org.spongepowered.api.block.transaction.Operation; @@ -54,7 +54,7 @@ public interface ChangeBlockEvent_AllMixin_Neo extends SpongeEventBridge_Neo { final List forgeEvents = new ArrayList<>(); for (final BlockTransaction transaction : thisEvent.transactions()) { if (player != null && transaction.operation() == breakOp) { - forgeEvents.add(new BlockEvent.BreakEvent((Level) thisEvent.world(), + forgeEvents.add(new BreakBlockEvent((Level) thisEvent.world(), VecHelper.toBlockPos(transaction.original().position()), (BlockState) transaction.original().state(), player)); } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/commands/CommandsMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/commands/CommandsMixin_Neo.java index 03fe56f3623..eb4ad1b5b98 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/commands/CommandsMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/commands/CommandsMixin_Neo.java @@ -29,11 +29,9 @@ import com.mojang.brigadier.tree.CommandNode; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.server.level.ServerPlayer; import net.neoforged.bus.api.Event; import net.neoforged.bus.api.IEventBus; -import net.neoforged.neoforge.server.command.CommandHelper; import org.spongepowered.api.command.CommandCause; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; @@ -46,21 +44,19 @@ import org.spongepowered.common.bridge.commands.CommandSourceStackBridge; import org.spongepowered.common.bridge.commands.CommandsBridge; import org.spongepowered.common.command.manager.SpongeCommandManager; +import org.spongepowered.common.command.resolver.SpongeSuggestionTreeResolver; import org.spongepowered.common.event.tracking.PhaseTracker; import java.util.IdentityHashMap; -import java.util.List; import java.util.Map; -import java.util.WeakHashMap; import java.util.function.Function; @Mixin(Commands.class) public abstract class CommandsMixin_Neo implements CommandsBridge { - private WeakHashMap, List>>> impl$playerNodeCache; private SpongeCommandManager impl$commandManager; - // The event fired by Forge is fired in ForgeCommandManager at the appropriate time. + // The event fired by Neo is fired in NeoCommandManager at the appropriate time. @Redirect(method = "performCommand", at = @At(value = "INVOKE", target = "Lnet/neoforged/bus/api/IEventBus;post(Lnet/neoforged/bus/api/Event;)Lnet/neoforged/bus/api/Event;")) private Event neo$redirectToSpongeCommandManager(IEventBus instance, Event event) { @@ -68,34 +64,28 @@ public abstract class CommandsMixin_Neo implements CommandsBridge { } @SuppressWarnings("unchecked") - @Redirect(method = "sendCommands", at = @At( - value = "INVOKE", - target = "Lnet/neoforged/neoforge/server/command/CommandHelper;mergeCommandNode(Lcom/mojang/brigadier/tree/CommandNode;Lcom/mojang/brigadier/tree/CommandNode;Ljava/util/Map;Ljava/lang/Object;Lcom/mojang/brigadier/Command;Ljava/util/function/Function;)V", - remap = false - )) + @Redirect(method = "sendCommands", at = @At(value = "INVOKE", target = "Lnet/neoforged/neoforge/server/command/CommandHelper;mergeCommandNode(Lcom/mojang/brigadier/tree/CommandNode;Lcom/mojang/brigadier/tree/CommandNode;Ljava/util/Map;Ljava/lang/Object;Lcom/mojang/brigadier/Command;Ljava/util/function/Function;)V")) private void impl$addNonBrigSuggestions( - final CommandNode sourceChild, - final CommandNode sourceNode, - final Map, CommandNode> resultNode, - final S sourceToResult, - final Command canUse, - final Function, SuggestionProvider> execute, + final CommandNode rootCommandNode, + final CommandNode rootSuggestion, + final Map, CommandNode> commandToSuggestion, + final S source, + final Command execute, + final Function, SuggestionProvider> commandToSuggestionFunction, final ServerPlayer player ) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(player); frame.addContext(EventContextKeys.SUBJECT, (Subject) player); - final CommandCause sourceToUse = ((CommandSourceStackBridge) sourceToResult).bridge$withCurrentCause(); - try { - this.impl$playerNodeCache.put(player, new IdentityHashMap<>()); - // We use this because the redirects should be a 1:1 mapping (which is what this map is for). - final IdentityHashMap, CommandNode> idMap = new IdentityHashMap<>(resultNode); - CommandHelper.mergeCommandNode(sourceChild, sourceNode, idMap, sourceToResult, canUse, execute); - } finally { - this.impl$playerNodeCache.remove(player); - } - for (final CommandNode node : this.impl$commandManager.getNonBrigadierSuggestions(sourceToUse)) { - sourceNode.addChild((CommandNode) node); + final CommandCause sourceToUse = ((CommandSourceStackBridge) source).bridge$withCurrentCause(); + + // We use this because the redirects should be a 1:1 mapping (which is what this map is for). + final IdentityHashMap, CommandNode> idMap = new IdentityHashMap(commandToSuggestion); + new SpongeSuggestionTreeResolver((CommandSourceStack) sourceToUse, idMap, new IdentityHashMap<>(), this.impl$commandManager) + .fillSuggestions((CommandNode) rootCommandNode, (CommandNode) rootSuggestion); + + for (final CommandNode node : this.impl$commandManager.getNonBrigadierSuggestions(sourceToUse)) { + rootSuggestion.addChild((CommandNode) node); } } } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/EntityTravelToDimensionEventMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/EntityTravelToDimensionEventMixin_Neo.java index 9ebe6e0685a..b62c3c1e774 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/EntityTravelToDimensionEventMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/EntityTravelToDimensionEventMixin_Neo.java @@ -69,7 +69,7 @@ public abstract class EntityTravelToDimensionEventMixin_Neo implements NeoEventB final Entity entity = ((EntityEvent) (Object) this).getEntity(); final ServerLevel toWorld = SpongeCommon.server().getLevel(this.dimension); return SpongeEventFactory.createChangeEntityWorldEventPre(PhaseTracker.getInstance().currentCause(), - (org.spongepowered.api.entity.Entity) entity, (org.spongepowered.api.world.server.ServerWorld) entity.getCommandSenderWorld(), + (org.spongepowered.api.entity.Entity) entity, (org.spongepowered.api.world.server.ServerWorld) entity.level(), (org.spongepowered.api.world.server.ServerWorld) toWorld, (org.spongepowered.api.world.server.ServerWorld) toWorld); } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/world/BlockEvent_BreakEventMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/world/BlockEvent_BreakEventMixin_Neo.java index ceec4ae8c05..cef5f076936 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/world/BlockEvent_BreakEventMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/world/BlockEvent_BreakEventMixin_Neo.java @@ -26,7 +26,7 @@ import net.minecraft.world.level.LevelAccessor; import net.neoforged.bus.api.ICancellableEvent; -import net.neoforged.neoforge.event.level.BlockEvent; +import net.neoforged.neoforge.event.level.block.BreakBlockEvent; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.BlockTypes; @@ -44,7 +44,7 @@ import java.util.Collections; -@Mixin(value = BlockEvent.BreakEvent.class, remap = false) +@Mixin(value = BreakBlockEvent.class, remap = false) public abstract class BlockEvent_BreakEventMixin_Neo extends BlockEventMixin_Neo implements NeoEventBridge_Neo, ICancellableEvent { @Override diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/registries/GameDataMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/registries/GameDataMixin_Neo.java index 90a19a2dce8..add30d4ac16 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/registries/GameDataMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/registries/GameDataMixin_Neo.java @@ -29,11 +29,16 @@ import net.minecraft.core.registries.Registries; import net.minecraft.world.entity.EntityType; import net.neoforged.neoforge.registries.GameData; +import org.spongepowered.api.Sponge; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.entity.SpongeEntityTypes; +import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.launch.Lifecycle; +import org.spongepowered.common.network.channel.SpongeChannelManager; +import org.spongepowered.common.network.packet.SpongePacketHandler; @Mixin(GameData.class) public class GameDataMixin_Neo { @@ -44,4 +49,28 @@ public class GameDataMixin_Neo { SpongeEntityTypes.register((Registry>) registry); } } + + @Inject(method = "postRegisterEvents", at = @At("HEAD")) + private static void neo$onRegisterEvents(final CallbackInfo ci) { + final Lifecycle lifecycle = Launch.instance().lifecycle(); + lifecycle.callConstructEvent(); + lifecycle.callRegisterFactoryEvent(); + lifecycle.callRegisterBuilderEvent(); + lifecycle.callRegisterChannelEvent(); + lifecycle.establishGameServices(); + lifecycle.establishDataKeyListeners(); + + SpongePacketHandler.init((SpongeChannelManager) Sponge.channelManager()); + + lifecycle.establishDataProviders(); + lifecycle.callRegisterDataEvent(); + lifecycle.establishEarlyGlobalRegistries(); + + Launch.instance().lifecycle().establishGlobalRegistries(); + } + + @Inject(method = "freezeData", at = @At("TAIL")) + private static void neo$onFreezeData(final CallbackInfo ci) { + Launch.instance().lifecycle().endEstablishGlobalRegistries(); + } } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/registries/RegistryManagerMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/registries/RegistryManagerMixin_Neo.java index 390cfd4613b..b7b8f24f372 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/registries/RegistryManagerMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/registries/RegistryManagerMixin_Neo.java @@ -24,7 +24,7 @@ */ package org.spongepowered.neoforge.mixin.core.neoforge.registries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.neoforged.neoforge.registries.RegistryManager; import net.neoforged.neoforge.registries.RegistrySnapshot; import org.spongepowered.asm.mixin.Mixin; @@ -40,7 +40,7 @@ public class RegistryManagerMixin_Neo { @Inject(method = "takeSnapshot", at = @At("RETURN")) - private static void neo$dontSendSpongeDataToClient(RegistryManager.SnapshotType snapshotType, CallbackInfoReturnable> cir) { + private static void neo$dontSendSpongeDataToClient(RegistryManager.SnapshotType snapshotType, CallbackInfoReturnable> cir) { if (snapshotType != RegistryManager.SnapshotType.SYNC_TO_CLIENT) { return; } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/network/ConnectionMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/network/ConnectionMixin_Neo.java index 3ba167337f6..16563ebfbb0 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/network/ConnectionMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/network/ConnectionMixin_Neo.java @@ -24,23 +24,39 @@ */ package org.spongepowered.neoforge.mixin.core.network; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; import net.minecraft.network.Connection; -import net.minecraft.network.PacketSendListener; +import net.minecraft.network.protocol.Packet; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.network.channel.PacketSender; @Mixin(Connection.class) public abstract class ConnectionMixin_Neo { - @Inject(method = "lambda$doSendPacket$13", at = @At(value = "INVOKE", target = "Lio/netty/util/concurrent/Future;isSuccess()Z")) - public void impl$onPacketSent(final PacketSendListener listener, final Future future, final CallbackInfo ci) { - if (listener instanceof final PacketSender.SpongePacketSendListener spongeListener) { - spongeListener.accept(future.cause()); + @WrapOperation(method = "doSendPacket", at = @At( + value = "INVOKE", + target = "Lio/netty/channel/ChannelFuture;addListener(Lio/netty/util/concurrent/GenericFutureListener;)Lio/netty/channel/ChannelFuture;", + remap = false + )) + public ChannelFuture impl$onPacketSent( + final ChannelFuture instance, final GenericFutureListener> genericFutureListener, + final Operation original, final Packet packet, + final ChannelFutureListener listener, final boolean abool + ) { + if (listener instanceof PacketSender.SpongePacketSendListener spsl) { + original.call(instance, (ChannelFutureListener) (future) -> { + if (!future.isSuccess()) { + spsl.accept(future.cause()); + } + }); } + return original.call(instance, listener); } } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/resources/RegistryDataLoaderMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/resources/RegistryDataLoaderMixin_Neo.java new file mode 100644 index 00000000000..07c1932ccf8 --- /dev/null +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/resources/RegistryDataLoaderMixin_Neo.java @@ -0,0 +1,126 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.mixin.core.resources; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.core.RegistryAccess; +import net.minecraft.resources.RegistryDataLoader; +import net.minecraft.resources.RegistryLoadTask; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.DependencySorter; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryRoots; +import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.accessor.resources.RegistryDataLoader_ResourceManagerRegistryLoadTaskAccessor; +import org.spongepowered.common.accessor.resources.RegistryLoadTaskAccessor; +import org.spongepowered.common.bridge.core.WritableRegistryBridge; +import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.launch.Lifecycle; +import org.spongepowered.common.registry.SpongeRegistryDependencyEntry; +import org.spongepowered.common.registry.SpongeRegistryHolder; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Mixin(RegistryDataLoader.class) +public class RegistryDataLoaderMixin_Neo { + + // NeoForge's lambda$load$2 has a gametest stream() call before the freeze logic. + // We use a @Slice starting after GameTestHooks.isGametestEnabled to skip it and + // target the freeze-step stream() call. Vanilla uses ordinal = 0 with no slice. + @SuppressWarnings({"rawtypes", "unchecked"}) + @WrapOperation(method = "lambda$load$2", + at = @At(value = "INVOKE", target = "Ljava/util/List;stream()Ljava/util/stream/Stream;", ordinal = 0), + slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/neoforged/fml/ModLoader;postEvent(Lnet/neoforged/bus/api/Event;)V"))) + private static Stream impl$onLoad( + List> instance, Operation original + ) { + // TODO: Remote layer + final DependencySorter, SpongeRegistryDependencyEntry>> dependencies = new DependencySorter<>(); + final Lifecycle lifecycle = Launch.instance().lifecycle(); + final var collect = instance.stream() + .collect(Collectors.groupingBy(l -> { + if (l instanceof RegistryDataLoader_ResourceManagerRegistryLoadTaskAccessor accessor) { + return accessor.accessor$getResourceManager(); + } + return ResourceManager.Empty.INSTANCE; + }, Collectors.toSet())); + collect + .forEach((k, v) -> { + final RegistryHolder registryHolder; + if (k == ResourceManager.Empty.INSTANCE) { + // on the client side, we don't have a real resource manager + registryHolder = SpongeCommon.game(); + } else { + registryHolder = (RegistryHolder) k; + } + // Build a combined registry list: new batch registries replace any existing ones + // with the same key (e.g., on re-join, the second load produces fresh registries + // for the same keys that were already in the root from the first load). + final var batchRegistries = v.stream() + .map(RegistryLoadTaskAccessor.class::cast) + .map(RegistryLoadTaskAccessor::accessor$registry) + .toList(); + final var batchKeys = batchRegistries.stream() + .map(net.minecraft.core.Registry::key) + .collect(java.util.stream.Collectors.toSet()); + ((SpongeRegistryHolder) registryHolder).setRootMinecraftRegistry(new RegistryAccess.ImmutableRegistryAccess( + (List) Stream.concat( + registryHolder.streamRegistries(RegistryRoots.MINECRAFT) + .filter(r -> !batchKeys.contains(((net.minecraft.core.Registry) r).key())), + batchRegistries.stream() + ).toList())); + + // Only fire plugin registry events on the server side (ResourceManager-backed loads). + // Client-side network loads only receive SYNCHRONIZED_REGISTRIES and lack server-only + // registries (placed_feature, configured_feature, etc.) that plugins may declare as + // dependencies, causing freeze validation to fail. + if (k != ResourceManager.Empty.INSTANCE) { + lifecycle.processServerRegistries(registryHolder, v.stream() + .map(RegistryLoadTaskAccessor.class::cast) + .filter(l -> !RegistryDataLoader.DIMENSION_REGISTRIES.contains(l.accessor$data())) // NOTE: DIMENSION_REGISTRIES are special! + .map(l -> (Registry) l.accessor$registry())); + } + + v + .stream().map(RegistryLoadTaskAccessor.class::cast) + .forEach(l -> dependencies.addEntry(((Registry) l.accessor$registry()).type(), + new SpongeRegistryDependencyEntry<>(l, ((WritableRegistryBridge) l.accessor$registry()).bridge$pendingDependencies().toList()))); + }); + + List> loaders = new ArrayList<>(collect.size()); + dependencies.orderByDependencies(($, v) -> loaders.add(v.cookie())); + + return original.call(loaders); + } +} diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/MinecraftServerMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/MinecraftServerMixin_Neo.java index 13c42aa2305..4f797bb9a4f 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/MinecraftServerMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/MinecraftServerMixin_Neo.java @@ -24,17 +24,29 @@ */ package org.spongepowered.neoforge.mixin.core.server; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.packs.resources.MultiPackResourceManager; import net.minecraft.world.level.Level; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Coerce; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.accessor.server.MinecraftServer_ReloadableResourcesAccessor; +import org.spongepowered.common.bridge.server.MinecraftServerBridge; import org.spongepowered.common.bridge.server.level.ServerLevelBridge; +import org.spongepowered.common.registry.SpongeRegistryHolder; import org.spongepowered.neoforge.NeoServer; +import java.util.Collection; + @Mixin(MinecraftServer.class) public abstract class MinecraftServerMixin_Neo implements NeoServer { @@ -50,4 +62,15 @@ public abstract class MinecraftServerMixin_Neo implements NeoServer { public long[] getTickTime(ResourceKey dim) { return ((ServerLevelBridge) this.shadow$getLevel(dim)).bridge$recentTickTimes(); } + + @ModifyExpressionValue(method = "lambda$reloadResources$1", at = @At(value = "NEW", target = "Lnet/minecraft/server/packs/resources/MultiPackResourceManager;")) + private MultiPackResourceManager neo$onReloadResources(final MultiPackResourceManager original) { + ((MinecraftServerBridge) this).bridge$reloadServerRegistries((RegistryHolder) original); + return original; + } + + @Inject(method = "lambda$reloadResources$4", at = @At("TAIL")) + public void neo$onReloadedResources(final Collection $$0x, final @Coerce MinecraftServer_ReloadableResourcesAccessor $$1x, final CallbackInfo ci) { + ((MinecraftServerBridge) this).bridge$reloadedServerRegistries(((SpongeRegistryHolder) $$1x.accessor$resourceManager()).registryHolder()); + } } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/ReloadableServerResourcesMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/ReloadableServerResourcesMixin_Neo.java new file mode 100644 index 00000000000..2b106c2543a --- /dev/null +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/ReloadableServerResourcesMixin_Neo.java @@ -0,0 +1,67 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.mixin.core.server; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.commands.Commands; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.LayeredRegistryAccess; +import net.minecraft.server.ReloadableServerResources; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.permissions.PermissionSet; +import net.minecraft.world.flag.FeatureFlagSet; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.bridge.server.packs.resources.ResourceManagerBridge; +import org.spongepowered.common.command.brigadier.dispatcher.DelegatingCommandDispatcher; + +import java.util.List; + +@Mixin(ReloadableServerResources.class) +public abstract class ReloadableServerResourcesMixin_Neo { + + @WrapOperation(method = "lambda$loadResources$2", at = @At(value = "NEW", + target = "(Lnet/minecraft/core/LayeredRegistryAccess;Lnet/minecraft/core/HolderLookup$Provider;Lnet/minecraft/world/flag/FeatureFlagSet;Lnet/minecraft/commands/Commands$CommandSelection;Ljava/util/List;Lnet/minecraft/server/permissions/PermissionSet;Ljava/util/List;)Lnet/minecraft/server/ReloadableServerResources;")) + private static ReloadableServerResources impl$onCreateResources( + final LayeredRegistryAccess fullLayers, + final HolderLookup.Provider loadingContext, + final FeatureFlagSet enabledFeatures, + final Commands.CommandSelection commandSelection, + final List postponedTags, + final PermissionSet functionCompilationPermissions, + final List newComponents, + final Operation original, + final @Local(argsOnly = true) ResourceManager resourceManager + ) { + final ReloadableServerResources instance = original.call(fullLayers, loadingContext, enabledFeatures, commandSelection, postponedTags, functionCompilationPermissions, newComponents); + if (instance.getCommands().getDispatcher() instanceof + final DelegatingCommandDispatcher delegatingCommandDispatcher) { + delegatingCommandDispatcher.permissionService(((ResourceManagerBridge) resourceManager).bridge$services().permissionService()); + } + return instance; + } +} diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/level/ServerEntityMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/level/ServerEntityMixin_Neo.java deleted file mode 100644 index bd1c9dafd96..00000000000 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/level/ServerEntityMixin_Neo.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.neoforge.mixin.core.server.level; - -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; -import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; -import net.minecraft.server.level.ServerEntity; -import net.minecraft.world.entity.Entity; -import net.neoforged.neoforge.network.bundle.PacketAndPayloadAcceptor; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.entity.living.human.HumanEntity; - -import java.util.EnumSet; -import java.util.List; - -@Mixin(ServerEntity.class) -public class ServerEntityMixin_Neo { - @Shadow @Final private Entity entity; - - /** - * @author gabizou - * @reason Because the entity spawn packet is just a lone packet, we have to actually - * do some hackery to create the player list packet first, then the spawn packet, - * then perform the remove packet. - */ - @Redirect( - method = "sendPairingData", - at = @At(value = "INVOKE", target = "Lnet/neoforged/neoforge/network/bundle/PacketAndPayloadAcceptor;accept(Lnet/minecraft/network/protocol/Packet;)Lnet/neoforged/neoforge/network/bundle/PacketAndPayloadAcceptor;", ordinal = 0) - ) - public PacketAndPayloadAcceptor impl$sendHumanSpawnPacket(PacketAndPayloadAcceptor consumer, Packet spawnPacket) { - if (!(this.entity instanceof final HumanEntity human)) { - return consumer.accept(spawnPacket); - } - // Adds the GameProfile to the client - consumer.accept(human.createPlayerListPacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER))); - // Actually spawn the human (a player) - consumer.accept(spawnPacket); - // Remove from the player map - final ClientboundPlayerInfoRemovePacket removePacket = new ClientboundPlayerInfoRemovePacket(List.of(human.getUUID())); - if (human.canRemoveFromListImmediately()) { - consumer.accept(removePacket); - } else { - // Human is a Player entity on the client and needs to tick once for the skin to render - human.removeFromTabListDelayed(null, removePacket); - } - return consumer; - } -} diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/level/ServerPlayerMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/level/ServerPlayerMixin_Neo.java index a3e3bd1629e..eeecdb3d90c 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/level/ServerPlayerMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/level/ServerPlayerMixin_Neo.java @@ -24,8 +24,10 @@ */ package org.spongepowered.neoforge.mixin.core.server.level; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; @@ -37,10 +39,14 @@ @Mixin(ServerPlayer.class) public abstract class ServerPlayerMixin_Neo extends LivingEntityMixin_Neo { + // @formatter:off + @Shadow public abstract ServerLevel shadow$level(); + // @formatter:on + // override from LivingEntityMixin_Neo @Override protected void neo$onElytraUse(final CallbackInfo ci) { - final PhaseContext context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext context = PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); final net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer) (Object) this; try (final EffectTransactor ignored = transactor.logPlayerInventoryChangeWithEffect(player, PlayerInventoryTransaction.EventCreator.STANDARD)) { diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Neo.java index 254941ffae0..ed98df8108d 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Neo.java @@ -27,6 +27,8 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.network.protocol.game.ServerGamePacketListener; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.world.inventory.RecipeBookMenu; import net.minecraft.world.item.crafting.RecipeHolder; @@ -36,6 +38,7 @@ import org.spongepowered.api.item.inventory.crafting.CraftingInventory; import org.spongepowered.api.item.inventory.query.QueryTypes; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.accessor.world.inventory.AbstractFurnaceMenuAccessor; @@ -47,37 +50,39 @@ @Mixin(ServerGamePacketListenerImpl.class) public abstract class ServerGamePacketListenerImplMixin_Neo implements ServerGamePacketListener { - @SuppressWarnings({"unchecked", "rawtypes"}) - @WrapOperation(method = "lambda$handlePlaceRecipe$10", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/RecipeBookMenu;handlePlacement(ZLnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/server/level/ServerPlayer;)V")) - private void neo$onPlaceRecipe( - final RecipeBookMenu recipeBookMenu, - final boolean shift, - final RecipeHolder recipe, - final net.minecraft.server.level.ServerPlayer player, - final Operation original + @Shadow public ServerPlayer player; + + @WrapOperation(method = "handlePlaceRecipe", + at = @At(value = "INVOKE", target = "net/minecraft/world/inventory/RecipeBookMenu.handlePlacement(ZZLnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/player/Inventory;)Lnet/minecraft/world/inventory/RecipeBookMenu$PostPlaceAction;")) + private RecipeBookMenu.PostPlaceAction neo$onPlaceRecipe( + final RecipeBookMenu recipeBookMenu, final boolean shift, final boolean isCreative, final RecipeHolder recipe, + final ServerLevel serverLevel, final net.minecraft.world.entity.player.Inventory inventory, + final Operation original ) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); + final var player = this.player; + final RecipeBookMenu.PostPlaceAction result; final var menu = (Inventory) player.containerMenu; if (menu instanceof AbstractFurnaceMenuAccessor afm && afm.accessor$container() instanceof BlockEntity be) { try (final EffectTransactor ignored = transactor.logPlaceCookingRecipe(be, shift, recipe, player)) { - original.call(recipeBookMenu, shift, recipe, player); + result = original.call(recipeBookMenu, shift,isCreative, recipe, serverLevel, inventory); player.containerMenu.broadcastChanges(); - return; + return result; } } final Inventory craftInv = ((Inventory) player.containerMenu).query(QueryTypes.INVENTORY_TYPE.get().of(CraftingInventory.class)); if (!(craftInv instanceof CraftingInventory)) { - original.call(recipeBookMenu, shift, recipe, player); + result = original.call(recipeBookMenu, shift,isCreative, recipe, serverLevel, inventory); SpongeCommon.logger().warn("Detected crafting without a InventoryCrafting!? Crafting Event will not fire."); - return; + return result; } try (final EffectTransactor ignored = transactor.logPlaceRecipe(shift, recipe, player, (CraftingInventory) craftInv)) { - original.call(recipeBookMenu, shift, recipe, player); + result = original.call(recipeBookMenu, shift,isCreative, recipe, serverLevel, inventory); player.containerMenu.broadcastChanges(); } + return result; } } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/tags/TagLoaderMixin_NeoForge.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/tags/TagLoaderMixin_NeoForge.java index 84462f8a94d..d7e27a18c78 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/tags/TagLoaderMixin_NeoForge.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/tags/TagLoaderMixin_NeoForge.java @@ -27,7 +27,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.tags.TagEntry; import net.minecraft.tags.TagLoader; import org.spongepowered.asm.mixin.Mixin; @@ -45,7 +45,7 @@ @Mixin(TagLoader.class) public abstract class TagLoaderMixin_NeoForge implements TagLoaderBridge { - @WrapOperation(method = "build(Lnet/minecraft/tags/TagEntry$Lookup;Ljava/util/List;)Lcom/mojang/datafixers/util/Either;", + @WrapOperation(method = "tryBuildTag(Lnet/minecraft/tags/TagEntry$Lookup;Ljava/util/List;)Lcom/mojang/datafixers/util/Either;", at = @At(value = "INVOKE", target = "Lnet/minecraft/tags/TagEntry;build(Lnet/minecraft/tags/TagEntry$Lookup;Ljava/util/function/Consumer;)Z")) protected boolean neo$onBuildTag(final TagEntry instance, final TagEntry.Lookup lookup, final Consumer consumer, final Operation original, final TagEntry.Lookup $$0, final List $$1, final @Local TagLoader.EntryWithSource entry) { @@ -55,13 +55,13 @@ public abstract class TagLoaderMixin_NeoForge implements TagLoaderBridge { return this.bridge$acceptTag(instance, lookup, consumer, original::call, $$1, entry); } - @Inject(method = "lambda$build$6", at = @At("HEAD")) - private void neo$onStartBuildingTag(final TagEntry.Lookup $$0x, final Map> $$1x, final ResourceLocation $$2x, + @Inject(method = "lambda$build$1", at = @At("HEAD")) + private void neo$onStartBuildingTag(final TagEntry.Lookup $$0x, final Map> $$1x, final Identifier $$2x, final @Coerce Object $$3x, final CallbackInfo ci) { this.bridge$buildingTagKey($$2x); } - @Inject(method = "lambda$build$6", at = @At("RETURN")) + @Inject(method = "lambda$build$1", at = @At("RETURN")) private void neo$onDoneBuildingTag(final CallbackInfo ci) { this.bridge$buildingTagKey(null); } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/EntityMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/EntityMixin_Neo.java new file mode 100644 index 00000000000..4db1c7d6257 --- /dev/null +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/EntityMixin_Neo.java @@ -0,0 +1,53 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.mixin.core.world.entity; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.damagesource.DamageSources; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LightningBolt; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(Entity.class) +public class EntityMixin_Neo { + + @Redirect(method = "thunderHit", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/damagesource/DamageSources;lightningBolt()Lnet/minecraft/world/damagesource/DamageSource;")) + private DamageSource vanilla$ThrowDamageEventWithLightingSource(final DamageSources sources, + final ServerLevel level, final LightningBolt lightningBolt + ) { + final var originalLightning = sources.lightningBolt(); + final var entitySource = org.spongepowered.api.event.cause.entity.damage.source.DamageSource.builder() + .from((org.spongepowered.api.event.cause.entity.damage.source.DamageSource) originalLightning) + .entity((org.spongepowered.api.entity.Entity) lightningBolt) + .build(); + return (DamageSource) (Object) entitySource; + } + +} diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo.java index cd25627c003..6ee51db2d4d 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo.java @@ -24,23 +24,84 @@ */ package org.spongepowered.neoforge.mixin.core.world.entity; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.cause.entity.MovementTypes; +import org.spongepowered.api.event.entity.MoveEntityEvent; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.event.ShouldFire; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.math.vector.Vector3d; @Mixin(value = LivingEntity.class) public abstract class LivingEntityMixin_Neo { + @Unique + @Nullable private Vector3d neo$preTeleportPosition; + + // Neo 26.2 replaced the vanilla hurtAndBreak call with its IItemStackExtension glide-damage hook. @Inject( method = "updateFallFlying", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/item/ItemStack;elytraFlightTick(Lnet/minecraft/world/entity/LivingEntity;I)Z", + target = "Lnet/minecraft/world/item/ItemStack;onGlideDamage(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/entity/EquipmentSlot;)V", shift = At.Shift.AFTER ) ) protected void neo$onElytraUse(final CallbackInfo ci) { } + + // Neo PR #3119 (26.1.2.31-beta) split randomTeleport into a 4-arg forwarder and a 5-arg overload that holds the body. + // Inject into the 5-arg overload so the original RETURN+BY 2 anchor still resolves. + @Inject(method = "randomTeleport(DDDZLnet/minecraft/world/item/ItemStack;)Z", at = @At("HEAD")) + private void neo$snapshotPositionBeforeVanillaTeleportLogic(final double x, final double y, final double z, final boolean changeState, + final ItemStack consumedStack, final CallbackInfoReturnable cir) { + final Entity self = (Entity) (Object) this; + this.neo$preTeleportPosition = new Vector3d(self.getX(), self.getY(), self.getZ()); + } + + @Inject(method = "randomTeleport(DDDZLnet/minecraft/world/item/ItemStack;)Z", + at = @At(value = "RETURN", ordinal = 0, shift = At.Shift.BY, by = 2), cancellable = true) + private void neo$callMoveEntityEventForTeleport(final double x, final double y, final double z, final boolean changeState, + final ItemStack consumedStack, final CallbackInfoReturnable cir) { + if (!ShouldFire.MOVE_ENTITY_EVENT) { + return; + } + + final Entity self = (Entity) (Object) this; + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + frame.pushCause(this); + + // ENTITY_TELEPORT is our fallback context + if (!frame.currentContext().containsKey(EventContextKeys.MOVEMENT_TYPE)) { + frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.ENTITY_TELEPORT); + } + + final MoveEntityEvent event = SpongeEventFactory.createMoveEntityEvent(frame.currentCause(), + (org.spongepowered.api.entity.Entity) this, this.neo$preTeleportPosition, + new Vector3d(self.getX(), self.getY(), self.getZ()), + new Vector3d(x, y, z)); + + if (SpongeCommon.post(event)) { + self.teleportTo(this.neo$preTeleportPosition.x(), this.neo$preTeleportPosition.y(), + this.neo$preTeleportPosition.z()); + cir.setReturnValue(false); + return; + } + + self.teleportTo(event.destinationPosition().x(), event.destinationPosition().y(), + event.destinationPosition().z()); + } + } } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo_Attack_Impl.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo_Attack_Impl.java deleted file mode 100644 index 298200b0516..00000000000 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo_Attack_Impl.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.neoforge.mixin.core.world.entity; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.LivingEntity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.util.DamageEventUtil; - -@Mixin(LivingEntity.class) -public abstract class LivingEntityMixin_Neo_Attack_Impl { - - protected DamageEventUtil.DamageEventResult attackImpl$actuallyHurtResult; - protected float attackImpl$actuallyHurtFinalDamage; - - /** - * Set absorbed damage after calling {@link LivingEntity#setAbsorptionAmount} in which we called the event - */ - @ModifyVariable(method = "actuallyHurt", ordinal = 2, - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;setAbsorptionAmount(F)V", shift = At.Shift.AFTER)) - public float attackImpl$setAbsorbed(final float value) { - if (this.attackImpl$actuallyHurtResult.event().isCancelled()) { - return 0; - } - return this.attackImpl$actuallyHurtResult.damageAbsorbed().orElse(0f); - } - - /** - * Prevents {@link ServerPlayer#awardStat} from running before event - */ - @Redirect(method = "getDamageAfterMagicAbsorb", - at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;awardStat(Lnet/minecraft/resources/ResourceLocation;I)V")) - public void attackImpl$onAwardStatDamageResist(final ServerPlayer instance, final ResourceLocation resourceLocation, final int i) { - // do nothing - } - -} diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo_Damage.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo_Damage.java new file mode 100644 index 00000000000..4dbbf866d4a --- /dev/null +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo_Damage.java @@ -0,0 +1,81 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.mixin.core.world.entity; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.common.damagesource.DamageContainer; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.common.bridge.world.entity.TrackedDamageBridge; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; + +import java.util.Stack; + +@Mixin(LivingEntity.class) +public abstract class LivingEntityMixin_Neo_Damage implements TrackedDamageBridge { + + @Shadow protected Stack damageContainers; + + @Shadow public abstract ItemStack shadow$getUseItem(); + + @Override + public float damage$getContainerDamage(final float damage) { + return this.damageContainers.peek().getNewDamage(); + } + + @Override + public void damage$setContainerDamage(final float damage) { + this.damageContainers.peek().setNewDamage(damage); + } + + @ModifyVariable(method = "actuallyHurt", ordinal = 1, + at = @At(value = "INVOKE_ASSIGN", target = "Lnet/neoforged/neoforge/common/CommonHooks;onLivingDamagePre(Lnet/minecraft/world/entity/LivingEntity;Lnet/neoforged/neoforge/common/damagesource/DamageContainer;)F")) + private float damage$modifyBeforeAbsorption(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.startStep(DamageStepTypes.ABSORPTION, damage, this); + } + + @WrapWithCondition(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/neoforged/neoforge/common/damagesource/DamageContainer;setReduction(Lnet/neoforged/neoforge/common/damagesource/DamageContainer$Reduction;F)V"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/neoforged/neoforge/common/CommonHooks;onLivingDamagePre(Lnet/minecraft/world/entity/LivingEntity;Lnet/neoforged/neoforge/common/damagesource/DamageContainer;)F"))) + private boolean damage$skipAbsorption(final DamageContainer container, final DamageContainer.Reduction reduction, final float absorbed) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null || !tracker.isSkipped(DamageStepTypes.ABSORPTION); + } + + // Neo 26.2 reuses the dmg parameter as the post-absorption damage (no separate local anymore), + // mirroring vanilla — target the parameter like LivingEntityMixin_Vanilla_Damage does. + @ModifyVariable(method = "actuallyHurt", at = @At("LOAD"), argsOnly = true, slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;awardStat(Lnet/minecraft/resources/Identifier;I)V"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getCombatTracker()Lnet/minecraft/world/damagesource/CombatTracker;"))) + private float damage$firePostEvent_Living(final float damage) { + return this.damage$firePostEvent(damage); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/WorldBorderCommandMixin.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/animal/golem/SnowGolemMixin_Neo.java similarity index 64% rename from src/mixins/java/org/spongepowered/common/mixin/core/server/commands/WorldBorderCommandMixin.java rename to neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/animal/golem/SnowGolemMixin_Neo.java index c29688d84ee..57c40a3356a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/WorldBorderCommandMixin.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/animal/golem/SnowGolemMixin_Neo.java @@ -22,29 +22,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.server.commands; +package org.spongepowered.neoforge.mixin.core.world.entity.animal.golem; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.commands.WorldBorderCommand; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.animal.golem.SnowGolem; +import net.neoforged.neoforge.event.EventHooks; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.bridge.world.entity.GrieferBridge; -@Mixin(WorldBorderCommand.class) -public abstract class WorldBorderCommandMixin { +@Mixin(SnowGolem.class) +public abstract class SnowGolemMixin_Neo { - @Redirect(method = { - "setDamageBuffer", - "setDamageAmount", - "setWarningTime", - "setWarningDistance", - "getSize", - "setCenter", - "setSize" - }, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;overworld()Lnet/minecraft/server/level/ServerLevel;")) - private static ServerLevel impl$useCurrentWorld(final MinecraftServer instance, final CommandSourceStack $$0) { - return $$0.getLevel(); + @Redirect(method = "aiStep()V", at = @At( + value = "INVOKE", target = "Lnet/neoforged/neoforge/event/EventHooks;canEntityGrief(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/Entity;)Z")) + private boolean impl$checkCanGrief(ServerLevel level, Entity entity) { + if (!((GrieferBridge) this).bridge$canGrief()) { + return false; + } + return EventHooks.canEntityGrief(level, entity); } } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/item/ItemEntityMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/item/ItemEntityMixin_Neo.java index 850ddc5dc63..5e30f96ee9c 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/item/ItemEntityMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/item/ItemEntityMixin_Neo.java @@ -46,7 +46,7 @@ public abstract class ItemEntityMixin_Neo { final CallbackInfo ci ) { // Check the level is not null to avoid an NPE - if (level != null && !level.isClientSide) { + if (level != null && !level.isClientSide()) { this.lifespan = SpongeGameConfigs.getForWorld(level).get().entity.item.despawnRate; } } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/player/PlayerMixin_Neo_Attack_Impl.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/player/PlayerMixin_Neo_Attack_Impl.java deleted file mode 100644 index b3b7af1204d..00000000000 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/player/PlayerMixin_Neo_Attack_Impl.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.neoforge.mixin.core.world.entity.player; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.event.entity.player.CriticalHitEvent; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; -import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.common.util.DamageEventUtil; -import org.spongepowered.neoforge.mixin.core.world.entity.LivingEntityMixin_Neo_Attack_Impl; - -@Mixin(Player.class) -public abstract class PlayerMixin_Neo_Attack_Impl extends LivingEntityMixin_Neo_Attack_Impl { - private DamageEventUtil.Attack attackImpl$attack; - - @WrapOperation(method = "attack", at = @At(value = "INVOKE", target = "Lnet/neoforged/neoforge/common/CommonHooks;fireCriticalHit(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/entity/Entity;ZF)Lnet/neoforged/neoforge/event/entity/player/CriticalHitEvent;")) - private CriticalHitEvent attackImpl$critHook(final Player player, final Entity target, final boolean vanillaCritical, final float damageModifier, final Operation original) { - final CriticalHitEvent event = original.call(player, target, vanillaCritical, damageModifier); - if (event.isCriticalHit()) { - this.attackImpl$attack.functions().add(DamageEventUtil.provideCriticalAttackFunction(this.attackImpl$attack.sourceEntity(), event.getDamageMultiplier())); - } - return event; - } - - /** - * Set absorbed damage after calling {@link Player#setAbsorptionAmount} in which we called the event - */ - @ModifyVariable(method = "actuallyHurt", ordinal = 2, - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;setAbsorptionAmount(F)V", shift = At.Shift.AFTER)) - public float attackImpl$setAbsorbed(final float value) { - if (this.attackImpl$actuallyHurtResult.event().isCancelled()) { - return 0; - } - return this.attackImpl$actuallyHurtResult.damageAbsorbed().orElse(0f); - } - - @ModifyVariable(method = "actuallyHurt", at = @At("LOAD"), ordinal = 3, slice = @Slice( - from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;awardStat(Lnet/minecraft/resources/ResourceLocation;I)V"), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;causeFoodExhaustion(F)V"))) - private float attackImpl$firePostEvent_Player(final float damage) { - if (this.attackImpl$actuallyHurtResult.event().isCancelled()) { - return 0; - } - return this.attackImpl$actuallyHurtFinalDamage; - } -} diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/player/PlayerMixin_Neo_Damage.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/player/PlayerMixin_Neo_Damage.java new file mode 100644 index 00000000000..b6ce129e2d1 --- /dev/null +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/player/PlayerMixin_Neo_Damage.java @@ -0,0 +1,73 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.mixin.core.world.entity.player; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.player.Player; +import net.neoforged.neoforge.common.damagesource.DamageContainer; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.common.bridge.world.entity.TrackedAttackBridge; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; +import org.spongepowered.neoforge.mixin.core.world.entity.LivingEntityMixin_Neo_Damage; + +@Mixin(Player.class) +public abstract class PlayerMixin_Neo_Damage extends LivingEntityMixin_Neo_Damage implements TrackedAttackBridge { + + @Shadow public abstract double shadow$entityInteractionRange(); + + @Override + public double attack$interactionRangeSquared() { + return Mth.square(this.shadow$entityInteractionRange()); + } + + @ModifyVariable(method = "actuallyHurt", ordinal = 1, + at = @At(value = "INVOKE_ASSIGN", target = "Lnet/neoforged/neoforge/common/CommonHooks;onLivingDamagePre(Lnet/minecraft/world/entity/LivingEntity;Lnet/neoforged/neoforge/common/damagesource/DamageContainer;)F")) + private float damage$modifyBeforeAbsorption(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.startStep(DamageStepTypes.ABSORPTION, damage, this); + } + + @WrapWithCondition(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/neoforged/neoforge/common/damagesource/DamageContainer;setReduction(Lnet/neoforged/neoforge/common/damagesource/DamageContainer$Reduction;F)V"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/neoforged/neoforge/common/CommonHooks;onLivingDamagePre(Lnet/minecraft/world/entity/LivingEntity;Lnet/neoforged/neoforge/common/damagesource/DamageContainer;)F"))) + private boolean damage$skipAbsorption(final DamageContainer container, final DamageContainer.Reduction reduction, final float absorbed) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null || !tracker.isSkipped(DamageStepTypes.ABSORPTION); + } + + // Neo 26.2 reuses the dmg parameter as the post-absorption damage; the old float ordinal 3 + // (absorbedDamage) is never loaded inside this slice anymore — target the parameter instead. + @ModifyVariable(method = "actuallyHurt", at = @At("LOAD"), argsOnly = true, slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;awardStat(Lnet/minecraft/resources/Identifier;I)V"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;causeFoodExhaustion(F)V"))) + private float damage$firePostEvent_Player(final float damage) { + return this.damage$firePostEvent(damage); + } +} diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/projectile/FishingHookMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/projectile/FishingHookMixin_Neo.java index e02aaf794ce..0e0e52b4e53 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/projectile/FishingHookMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/projectile/FishingHookMixin_Neo.java @@ -45,7 +45,7 @@ public abstract class FishingHookMixin_Neo extends ProjectileMixin { if (SpongeCommonEventFactory.handleCollideImpactEvent(self, this.impl$getProjectileSource(), hitResult)) { this.shadow$discard(); } else { - this.onHit(hitResult); + this.shadow$onHit(hitResult); } } } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/vehicle/BoatMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/vehicle/boat/AbstractBoatMixin_Neo.java similarity index 90% rename from neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/vehicle/BoatMixin_Neo.java rename to neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/vehicle/boat/AbstractBoatMixin_Neo.java index 542343f5564..e42d84f0743 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/vehicle/BoatMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/vehicle/boat/AbstractBoatMixin_Neo.java @@ -22,21 +22,21 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge.mixin.core.world.entity.vehicle; +package org.spongepowered.neoforge.mixin.core.world.entity.vehicle.boat; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.entity.vehicle.boat.AbstractBoat; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.bridge.world.entity.vehicle.BoatBridge; +import org.spongepowered.common.bridge.world.entity.vehicle.AbstractBoatBridge; -@Mixin(Boat.class) -public abstract class BoatMixin_Neo implements BoatBridge { +@Mixin(AbstractBoat.class) +public abstract class AbstractBoatMixin_Neo implements AbstractBoatBridge { @Redirect(method = "getGroundFriction", at = @At( value = "INVOKE", diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/commands/SpreadPlayersCommandMixin_Forge.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/item/TeleportRandomlyConsumeEffectMixin_Neo.java similarity index 62% rename from forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/commands/SpreadPlayersCommandMixin_Forge.java rename to neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/item/TeleportRandomlyConsumeEffectMixin_Neo.java index 6fa2c09674b..ebdc6e47bfa 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/commands/SpreadPlayersCommandMixin_Forge.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/item/TeleportRandomlyConsumeEffectMixin_Neo.java @@ -22,12 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.mixin.core.server.commands; +package org.spongepowered.neoforge.mixin.core.world.item; -import net.minecraft.server.commands.SpreadPlayersCommand; -import net.minecraft.world.entity.Entity; -import net.minecraftforge.event.ForgeEventFactory; -import net.minecraftforge.event.entity.EntityTeleportEvent; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.cause.entity.MovementTypes; @@ -36,18 +35,20 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.event.tracking.PhaseTracker; -@Mixin(SpreadPlayersCommand.class) -public abstract class SpreadPlayersCommandMixin_Forge { +// Neo PR #3119 (26.1.2.31-beta) changed TeleportRandomlyConsumeEffect#apply to call the new +// 5-arg randomTeleport(...,ItemStack) overload directly so it can fire EntityTeleportEvent.ItemConsumption. +// Redirect the 5-arg call here. +@Mixin(TeleportRandomlyConsumeEffect.class) +public abstract class TeleportRandomlyConsumeEffectMixin_Neo { - @Redirect(method = "setPlayerPositions", at = @At( - value = "INVOKE", - target = "Lnet/minecraftforge/event/ForgeEventFactory;onEntityTeleportSpreadPlayersCommand(Lnet/minecraft/world/entity/Entity;DDD)Lnet/minecraftforge/event/entity/EntityTeleportEvent$SpreadPlayersCommand;" - )) - private static EntityTeleportEvent.SpreadPlayersCommand vanilla$createCauseFrameForTeleport(Entity entity, double targetX, double targetY, double targetZ) { + @Redirect(method = "apply", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;randomTeleport(DDDZLnet/minecraft/world/item/ItemStack;)Z")) + private boolean neo$createCauseFrameForTeleport(final LivingEntity entity, final double x, final double y, final double z, + final boolean changeState, final ItemStack consumedStack) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.COMMAND); + frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.CHORUS_FRUIT.get()); - return ForgeEventFactory.onEntityTeleportSpreadPlayersCommand(entity, targetX, targetY, targetZ); + return entity.randomTeleport(x, y, z, changeState, consumedStack); } } } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/item/crafting/RecipeManagerMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/item/crafting/RecipeManagerMixin_Neo.java new file mode 100644 index 00000000000..0ab4a8efa59 --- /dev/null +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/item/crafting/RecipeManagerMixin_Neo.java @@ -0,0 +1,68 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.mixin.core.world.item.crafting; + +import com.google.gson.JsonElement; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DynamicOps; +import net.minecraft.resources.FileToIdConverter; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeManager; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.registry.RegistryHolderLogic; +import org.spongepowered.common.registry.SpongeRegistryHolder; + +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.function.Consumer; +import java.util.stream.Stream; + +@Mixin(RecipeManager.class) +public abstract class RecipeManagerMixin_Neo { + + @SuppressWarnings("unchecked") + @WrapOperation(method = "prepare(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)Lnet/minecraft/world/item/crafting/RecipeMap;", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/SimpleJsonResourceReloadListener;scanDirectoryWithModifier(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/resources/FileToIdConverter;Lcom/mojang/serialization/DynamicOps;Lcom/mojang/serialization/Codec;Ljava/util/Map;Ljava/util/function/Consumer;)V")) + private void impl$onPrepare(final ResourceManager $$0, final FileToIdConverter $$1, final DynamicOps $$2, final Codec> $$3, + final Map> $$4, final Consumer> $$5, final Operation original) { + SortedMap> result = new TreeMap<>(); + original.call($$0, $$1, $$2, $$3, result, $$5); + final RegistryHolderLogic registryHolder = ((SpongeRegistryHolder) $$0).registryHolder(); + final Registry> registry = (Registry>) (Object) registryHolder.registry(RegistryTypes.RECIPE); + result.forEach((k, v) -> registry.register((org.spongepowered.api.ResourceKey) (Object) k, v)); + Launch.instance().lifecycle().processServerRegistries((RegistryHolder) $$0, Stream.of(registry)); + registry.streamEntries().forEach(e -> $$4.put((Identifier) (Object) e.key(), e.value())); + } +} diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/level/ServerExplosionMixin_NeoForge.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/level/ServerExplosionMixin_NeoForge.java new file mode 100644 index 00000000000..167f6f922ba --- /dev/null +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/level/ServerExplosionMixin_NeoForge.java @@ -0,0 +1,116 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.neoforge.mixin.core.world.level; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.ServerExplosion; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.world.ExplosionEvent; +import org.spongepowered.api.world.explosion.Explosion; +import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.api.world.server.ServerWorld; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.event.ShouldFire; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.util.VecHelper; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Mixin(ServerExplosion.class) +public abstract class ServerExplosionMixin_NeoForge { + + + // @formatter:off + @Shadow @Final private ServerLevel level; + + @Shadow protected abstract boolean shadow$interactsWithBlocks(); + // @formatter:on + + /** + * These two fields are used in the mixin below, but require a redirect specific for SpongeVanilla + * and SpongeForge as NeoForge makes a change to the method being used here. + * + * @see org.spongepowered.common.mixin.core.world.level.ServerExplosionMixin + */ + private boolean impl$shouldDamageEntities; + private double impl$knockbackMultiplier; + private List impl$affectedBlocks; + + @Redirect(method = "hurtEntities(Ljava/util/List;)V", at = @At(value = "INVOKE", + target = "net/minecraft/server/level/ServerLevel.getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;")) + private List neo$throwEventForHurtEntities(final ServerLevel instance, final Entity sourceEntity, final AABB aabb) { + // NeoForge hands this list to EventHooks.onExplosionDetonate where mods may mutate it, + // so every list returned here must be mutable. + final List entities; + if (this.impl$shouldDamageEntities) { + // filter out invulnerable entities before event + entities = instance.getEntities(sourceEntity, aabb).stream() + .filter(e -> !e.ignoreExplosion((net.minecraft.world.level.Explosion) this)) + .collect(Collectors.toCollection(ArrayList::new)); + } else { + entities = new ArrayList<>(); + } + + if (ShouldFire.EXPLOSION_EVENT_DETONATE) { + final var apiWorld = (ServerWorld) this.level; + final var apiEntities = entities.stream().map(org.spongepowered.api.entity.Entity.class::cast).collect(Collectors.toCollection(ArrayList::new)); + final var apiBlockPositions = this.impl$affectedBlocks.stream().map(bp -> ServerLocation.of(apiWorld, VecHelper.toVector3i(bp))).toList(); + final Cause cause = PhaseTracker.getInstance().currentCause(); + final ExplosionEvent.Detonate event = SpongeEventFactory.createExplosionEventDetonate(cause, apiBlockPositions, apiEntities, (Explosion) this, apiWorld); + if (SpongeCommon.post(event)) { + this.impl$affectedBlocks.clear(); // no blocks affected + return new ArrayList<>(); // no entities affected + } + if (this.shadow$interactsWithBlocks()) { + this.impl$affectedBlocks = event.affectedLocations().stream().map(VecHelper::toBlockPos).collect(Collectors.toList()); + } + if (this.impl$shouldDamageEntities) { + return event.entities().stream().map(Entity.class::cast).collect(Collectors.toCollection(ArrayList::new)); + } + } + return entities; + } + + @WrapOperation(method = "hurtEntities(Ljava/util/List;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/Vec3;scale(D)Lnet/minecraft/world/phys/Vec3;")) + private Vec3 neo$useKnockbackMultiplier(Vec3 instance, double $$0, Operation original) { + // Honor our knockback value from event + return original.call(instance, $$0 * this.impl$knockbackMultiplier); + } + +} diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/level/block/FireBlockMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/level/block/FireBlockMixin_Neo.java index f81da0b2646..513efba73b7 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/level/block/FireBlockMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/level/block/FireBlockMixin_Neo.java @@ -49,7 +49,7 @@ public class FireBlockMixin_Neo { cancellable = true) private void impl$onCatchFirePreCheck( final Level world, final BlockPos pos, final int chance, final RandomSource random, final int age, final Direction dir, final CallbackInfo callbackInfo) { - if (!world.isClientSide) { + if (!world.isClientSide()) { if (ShouldFire.CHANGE_BLOCK_EVENT_PRE && SpongeCommonEventFactory.callChangeBlockEventPre((ServerLevelBridge) world, pos).isCancelled()) { callbackInfo.cancel(); } @@ -65,7 +65,7 @@ public class FireBlockMixin_Neo { cancellable = true) private void impl$onCatchFirePreCheckOther( final Level world, final BlockPos pos, final int chance, final RandomSource random, final int age, final Direction dir, final CallbackInfo callbackInfo) { - if (!world.isClientSide) { + if (!world.isClientSide()) { if (ShouldFire.CHANGE_BLOCK_EVENT_PRE && SpongeCommonEventFactory.callChangeBlockEventPre((ServerLevelBridge) world, pos).isCancelled()) { callbackInfo.cancel(); } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Neo.java index 53a30f28e95..ec8da3607c9 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Neo.java @@ -24,16 +24,18 @@ */ package org.spongepowered.neoforge.mixin.core.world.level.block.entity; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; -import net.minecraft.core.RegistryAccess; +import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.AbstractCookingRecipe; import net.minecraft.world.item.crafting.RecipeHolder; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; import net.minecraft.world.level.block.state.BlockState; -import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.Opcodes; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.block.entity.carrier.furnace.FurnaceBlockEntity; @@ -49,10 +51,12 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.block.entity.AbstractFurnaceBlockEntityBridge; import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.event.tracking.phase.general.GeneralPhase; +import org.spongepowered.common.event.tracking.phase.general.RecipeContext; import org.spongepowered.common.inventory.adapter.impl.slots.SlotAdapter; import org.spongepowered.common.item.util.ItemStackUtil; import org.spongepowered.common.mixin.core.world.level.block.entity.BaseContainerBlockEntityMixin; @@ -67,57 +71,98 @@ public abstract class AbstractFurnaceBlockEntityMixin_Neo extends BaseContainerB // @formatter:off @Shadow protected NonNullList items; - @Shadow int cookingProgress; - @Shadow private static boolean shadow$canBurn(final RegistryAccess registryAccess, @Nullable final RecipeHolder recipe, final NonNullList slots, final int maxStackSize, final AbstractFurnaceBlockEntity furnace) { - throw new UnsupportedOperationException("Shadowed canBurn"); - } + @Shadow private int cookingTimer; // @formatter:on + private static final ThreadLocal switched = new ThreadLocal<>(); private boolean neo$filledWaterBucket; // Tick up and Start - @Redirect(method = "serverTick", + @WrapOperation(method = "serverTick", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;canBurn(Lnet/minecraft/core/RegistryAccess;Lnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/core/NonNullList;ILnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;)Z", - ordinal = 1)) - private static boolean neo$checkIfCanSmelt(final RegistryAccess registryAccess, @Nullable final RecipeHolder recipe, final NonNullList slots, final int maxStackSize, final AbstractFurnaceBlockEntity entityIn) { - if (!AbstractFurnaceBlockEntityMixin_Neo.shadow$canBurn(registryAccess, recipe, slots, maxStackSize, entityIn)) { + target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;canBurn(Lnet/minecraft/core/NonNullList;ILnet/minecraft/world/item/ItemStack;)Z" + ) + ) + private static boolean neo$checkIfCanSmelt( + final NonNullList items, + final int maxStackSize, + final ItemStack burnResult, + final Operation original, + final ServerLevel level, + final BlockPos pos, + final BlockState state, + final AbstractFurnaceBlockEntity entityIn, + final @Local RecipeHolder recipe + ) { + if (!original.call(items, maxStackSize, burnResult)) { return false; } - final var entity = (AbstractFurnaceBlockEntityMixin_Neo) (Object) (entityIn); - final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(slots.get(1)); + final var cookingRecipe = recipe.value(); + final var entity = (AbstractFurnaceBlockEntityMixin_Neo) (Object) (entityIn); + final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(items.get(1)); final Cause cause = PhaseTracker.getInstance().currentCause(); - if (entity.cookingProgress == 0) { // Start + final var identifier = recipe.id().identifier(); + // Prepare a phase stack to capture the recipe in use + final var context = GeneralPhase.State.RECIPE_CRAFTING.createPhaseContext(PhaseTracker.getInstance()) + .recipe(recipe.value()) + .recipeID(identifier) + .furnace(entityIn) + .provideItem(items.get(1)) + .buildAndSwitch(); + switched.set(context); + if (entity.cookingTimer == 0) { // Start final CookingEvent.Start event = SpongeEventFactory.createCookingEventStart(cause, (FurnaceBlockEntity) entityIn, Optional.of(fuel), - Optional.of((CookingRecipe) recipe.value()), Optional.of((ResourceKey) (Object) recipe.id())); + Optional.of((CookingRecipe) cookingRecipe), Optional.of((ResourceKey) (Object) identifier)); SpongeCommon.post(event); return !event.isCancelled(); } else { // Tick up final ItemStackSnapshot cooking = ItemStackUtil.snapshotOf(entity.items.get(0)); final CookingEvent.Tick event = SpongeEventFactory.createCookingEventTick(cause, (FurnaceBlockEntity) entityIn, cooking, Optional.of(fuel), - Optional.of((CookingRecipe) recipe.value()), Optional.of((ResourceKey) (Object) recipe.id())); + Optional.of((CookingRecipe) cookingRecipe), Optional.of((ResourceKey) (Object) identifier)); SpongeCommon.post(event); return !event.isCancelled(); } } + @Inject(method = "serverTick", at = @At("RETURN")) + private static void neo$closeRecipeContext( + final ServerLevel level, + final BlockPos pos, final BlockState state, + final AbstractFurnaceBlockEntity entity, + final CallbackInfo ci) { + if (switched.get() != null && PhaseTracker.getInstance().getPhaseContext() != switched.get()) { + SpongeCommon.logger().warn("Closed a recipe context that wasn't open!"); + } + final var closing = switched.get(); + switched.remove(); + if (closing != null) { + closing.close(); + } + } + // Tick down @Redirect(method = "serverTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;clamp(III)I")) - private static int neo$resetCookTimeIfCancelled(final int newCookTime, final int zero, final int totalCookTime, - final Level level, final BlockPos entityPos, final BlockState state, final AbstractFurnaceBlockEntity entityIn) { + private static int neo$resetCookTimeIfCancelled( + final int newCookTime, final int zero, final int totalCookTime, + final ServerLevel level, final BlockPos entityPos, final BlockState state, + final AbstractFurnaceBlockEntity entityIn) { final int clampedCookTime = Mth.clamp(newCookTime, zero, totalCookTime); final var entity = (AbstractFurnaceBlockEntityMixin_Neo) (Object) entityIn; - final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(entity.items.get(1)); + if (PhaseTracker.getInstance().getPhaseContext() != switched.get()) { + // we're in an invariant state + return clampedCookTime; + } + final var fuel = switched.get().usedItem(); final Cause cause = PhaseTracker.getInstance().currentCause(); - final var recipe = entity.bridge$getCurrentRecipe(); + final var recipe = switched.get().recipe(); final ItemStackSnapshot cooking = ItemStackUtil.snapshotOf(entity.items.get(0)); final CookingEvent.Tick event = SpongeEventFactory.createCookingEventTick(cause, (FurnaceBlockEntity) entityIn, cooking, Optional.of(fuel), - recipe.map(r -> (CookingRecipe) r.value()), recipe.map(r -> (ResourceKey) (Object) r.id())); + Optional.of((CookingRecipe) recipe), Optional.of((ResourceKey) (Object) switched.get().recipeID())); SpongeCommon.post(event); if (event.isCancelled()) { - return entity.cookingProgress; // dont tick down + return entity.cookingTimer; // dont tick down } return clampedCookTime; @@ -126,21 +171,31 @@ public abstract class AbstractFurnaceBlockEntityMixin_Neo extends BaseContainerB // Finish @Inject(method = "burn", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/NonNullList;set(ILjava/lang/Object;)Ljava/lang/Object;"), slice = @Slice( - from = @At(value = "FIELD", target = "Lnet/minecraft/world/level/block/Blocks;WET_SPONGE:Lnet/minecraft/world/level/block/Block;", opcode = Opcodes.GETSTATIC) + from = @At(value = "FIELD", target = "Lnet/minecraft/world/item/Items;WET_SPONGE:Lnet/minecraft/world/item/Item;", opcode = Opcodes.GETSTATIC) )) - private static void neo$captureBucketFill(final RegistryAccess registryAccess, final RecipeHolder recipe, final NonNullList slots, final int maxStackSize, final AbstractFurnaceBlockEntity entityIn, final CallbackInfoReturnable cir) { - ((AbstractFurnaceBlockEntityMixin_Neo) (Object) entityIn).neo$filledWaterBucket = true; + private static void neo$captureBucketFill( + final NonNullList items, final ItemStack inputItemStack, final ItemStack result, final CallbackInfo ci + ) { + final Cause cause = PhaseTracker.getInstance().currentCause(); + final FurnaceBlockEntity entity = cause.first(FurnaceBlockEntity.class) + .orElseThrow(() -> new IllegalStateException("Expected to have a FurnaceBlockEntity in the Cause")); + ((AbstractFurnaceBlockEntityMixin_Neo) entity).neo$filledWaterBucket = true; } @Inject(method = "burn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;shrink(I)V", shift = At.Shift.AFTER)) - private static void neo$afterSmeltItem(final RegistryAccess registryAccess, final RecipeHolder recipe, final NonNullList slots, final int maxStackSize, final AbstractFurnaceBlockEntity entityIn, final CallbackInfoReturnable cir) { - final ItemStack itemIn = slots.get(0); - final ItemStack recipeResult = recipe.value().getResultItem(registryAccess); - final ItemStack itemOut = slots.get(2); + private static void neo$afterSmeltItem( + final NonNullList items, final ItemStack inputItemStack, final ItemStack result, final CallbackInfo ci + ) { + final ItemStack itemIn = items.get(0); + final ItemStack itemOut = items.get(2); + if (PhaseTracker.getInstance().getPhaseContext() != switched.get()) { + return; + } final Cause cause = PhaseTracker.getInstance().currentCause(); - final FurnaceBlockEntity entity = (FurnaceBlockEntity) entityIn; - + final FurnaceBlockEntity entity = (FurnaceBlockEntity) switched.get().furnace(); + final var recipe = switched.get().recipe(); + final var recipeID = switched.get().recipeID(); final List transactions = new ArrayList<>(); itemIn.grow(1); final ItemStackSnapshot originalSmeltItem = ItemStackUtil.snapshotOf(itemIn); @@ -149,25 +204,37 @@ public abstract class AbstractFurnaceBlockEntityMixin_Neo extends BaseContainerB final boolean hasFuel = !((AbstractFurnaceBlockEntityMixin_Neo) entity).neo$filledWaterBucket; if (((AbstractFurnaceBlockEntityMixin_Neo) entity).neo$filledWaterBucket) { - transactions.add(new SlotTransaction(entity.inventory().slot(1).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(slots.get(1)))); + transactions.add(new SlotTransaction(entity.inventory().slot(1).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(items.get(1)))); } ((AbstractFurnaceBlockEntityMixin_Neo) entity).neo$filledWaterBucket = false; if (itemOut.isEmpty()) { - transactions.add(new SlotTransaction(entity.inventory().slot(2).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(recipeResult))); - } else if (ItemStack.isSameItemSameComponents(itemOut, recipeResult)) { + transactions.add(new SlotTransaction(entity.inventory().slot(2).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(result))); + } else if (ItemStack.isSameItemSameComponents(itemOut, result)) { itemOut.shrink(1); final ItemStackSnapshot originalResult = ItemStackUtil.snapshotOf(itemOut); itemOut.grow(1); transactions.add(new SlotTransaction(entity.inventory().slot(2).get(), originalResult, ItemStackUtil.snapshotOf(itemOut))); } - final Optional fuel = hasFuel && !slots.get(1).isEmpty() ? Optional.of(ItemStackUtil.snapshotOf(slots.get(1))) : Optional.empty(); + final Optional fuel = hasFuel && !items.get(1).isEmpty() ? Optional.of(ItemStackUtil.snapshotOf(items.get(1))) : Optional.empty(); final CookingEvent.Finish event = SpongeEventFactory.createCookingEventFinish(cause, entity, - fuel, Optional.of((CookingRecipe) recipe.value()), Optional.of((ResourceKey) (Object) recipe.id()), Collections.unmodifiableList(transactions)); + fuel, Optional.of((CookingRecipe) recipe), Optional.of((ResourceKey) (Object) recipeID), Collections.unmodifiableList(transactions)); SpongeCommon.post(event); for (final SlotTransaction transaction : transactions) { - transaction.custom().ifPresent(item -> slots.set(((SlotAdapter) transaction.slot()).getOrdinal(), ItemStackUtil.fromSnapshotToNative(item))); + transaction.custom().ifPresent(item -> items.set(((SlotAdapter) transaction.slot()).getOrdinal(), ItemStackUtil.fromSnapshotToNative(item))); } } + + // Interrupt-Active - e.g. a player removing the currently smelting item + @Inject( + method = "setItem(ILnet/minecraft/world/item/ItemStack;Z)V", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;getTotalCookTime(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;)I" + ) + ) + private void neo$interruptSmelt(final CallbackInfo ci) { + this.impl$callInterruptSmeltEvent(); + } } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/inventory/event/server/level/ServerPlayerMixin_Inventory_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/inventory/event/server/level/ServerPlayerMixin_Inventory_Neo.java index c327086cdf5..3e6a4de8927 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/inventory/event/server/level/ServerPlayerMixin_Inventory_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/inventory/event/server/level/ServerPlayerMixin_Inventory_Neo.java @@ -28,6 +28,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Cancellable; import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.player.Player; @@ -35,6 +36,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -49,12 +51,14 @@ import java.util.function.Consumer; @Mixin(ServerPlayer.class) -public abstract class ServerPlayerMixin_Inventory_Neo extends PlayerMixin_Inventory_Neo { +public abstract class ServerPlayerMixin_Inventory_Neo extends PlayerMixin_Inventory_Neo { // @formatter:off - @Nullable private Object inventory$menuProvider; + @Shadow public abstract ServerLevel shadow$level(); // @formatter:on + @Nullable private Object inventory$menuProvider; + @Inject( method = "openMenu(Lnet/minecraft/world/MenuProvider;Ljava/util/function/Consumer;)Ljava/util/OptionalInt;", at = @At( @@ -65,7 +69,7 @@ public abstract class ServerPlayerMixin_Inventory_Neo extends PlayerMixin_Invent ) ) private void impl$afterOpenMenu(final CallbackInfoReturnable cir) { - PhaseTracker.SERVER.getPhaseContext().getTransactor().logContainerSet((ServerPlayer) (Object) this); + PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext().getTransactor().logContainerSet((ServerPlayer) (Object) this); } @Inject(method = "openMenu(Lnet/minecraft/world/MenuProvider;Ljava/util/function/Consumer;)Ljava/util/OptionalInt;", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;initMenu(Lnet/minecraft/world/inventory/AbstractContainerMenu;)V")) @@ -73,7 +77,12 @@ public abstract class ServerPlayerMixin_Inventory_Neo extends PlayerMixin_Invent this.inventory$menuProvider = menuProvider; } - @Inject(method = "openMenu(Lnet/minecraft/world/MenuProvider;Ljava/util/function/Consumer;)Ljava/util/OptionalInt;", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;closeContainer()V", shift = At.Shift.AFTER), cancellable = true) + // Neo 26.2 splits the close path on MenuProvider#shouldTriggerClientSideContainerClosingOnOpen, + // so a cancelled close must be detected after either branch. + @Inject(method = "openMenu(Lnet/minecraft/world/MenuProvider;Ljava/util/function/Consumer;)Ljava/util/OptionalInt;", at = { + @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;closeContainer()V", shift = At.Shift.AFTER), + @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;doCloseContainer()V", shift = At.Shift.AFTER) + }, cancellable = true) private void impl$openMenuCloseCancelled(final CallbackInfoReturnable cir) { if (this.containerMenu != this.inventoryMenu) { cir.setReturnValue(OptionalInt.empty()); @@ -91,7 +100,7 @@ public abstract class ServerPlayerMixin_Inventory_Neo extends PlayerMixin_Invent final MenuProvider menuProvider, final int containerCounter, final net.minecraft.world.entity.player.Inventory inventory, final Player player, final Operation original, final @Cancellable CallbackInfoReturnable cir ) { - final PhaseContext context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext context = PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext(); try (final EffectTransactor ignored = context.getTransactor().logOpenInventory((ServerPlayer) (Object) this)) { final AbstractContainerMenu menu = original.call(menuProvider, containerCounter, inventory, player); context.containerLocation().ifPresent(((ContainerBridge) menu)::bridge$setOpenLocation); diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory_Neo.java deleted file mode 100644 index 6ec63c229c9..00000000000 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory_Neo.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.neoforge.mixin.inventory.event.world.level.block.entity; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.Container; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.HopperBlockEntity; -import org.spongepowered.api.item.inventory.Inventory; -import org.spongepowered.api.item.inventory.transaction.SlotTransaction; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import org.spongepowered.common.accessor.world.level.block.entity.HopperBlockEntityAccessor; -import org.spongepowered.common.bridge.world.inventory.container.TrackedInventoryBridge; -import org.spongepowered.common.event.ShouldFire; -import org.spongepowered.common.event.inventory.InventoryEventFactory; -import org.spongepowered.common.inventory.util.InventoryUtil; - -@Mixin(HopperBlockEntity.class) -public abstract class HopperBlockEntityMixin_Inventory_Neo { - - @Redirect(method = "ejectItems", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;isFullContainer(Lnet/minecraft/world/Container;Lnet/minecraft/core/Direction;)Z")) - private static boolean neo$throwTransferPreIfNotFull( - final Container attachedContainer, final Direction direction, final Level level, final BlockPos pos, final HopperBlockEntity block - ) { - final boolean result = HopperBlockEntityAccessor.invoker$isFullContainer(attachedContainer, direction); - if (result || !ShouldFire.TRANSFER_INVENTORY_EVENT_PRE) { - return result; - } - return InventoryEventFactory.callTransferPre(InventoryUtil.toInventory(block), InventoryUtil.toInventory(attachedContainer)).isCancelled(); - } - -// Post Captured Transactions - - @Inject(method = "ejectItems", locals = LocalCapture.CAPTURE_FAILHARD, - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z", - ordinal = 1)) - private static void neo$afterPutStackInSlots( - final Level var0, final BlockPos var1, final HopperBlockEntity var3, - final CallbackInfoReturnable cir, final Container iInventory, final Direction enumFacing, - final int i, final ItemStack itemStack, final int count,final ItemStack itemStack1 - ) { - // after putStackInInventoryAllSlots if the transfer worked - if (ShouldFire.TRANSFER_INVENTORY_EVENT_POST && itemStack1.isEmpty()) { - // Capture Insert in Origin - final TrackedInventoryBridge capture = InventoryUtil.forCapture(var3); - final SlotTransaction sourceSlotTransaction = InventoryEventFactory.captureTransaction(capture, (Inventory) var3, i, itemStack); - // Call event - InventoryEventFactory.callTransferPost(capture, (Inventory) iInventory, InventoryUtil.toInventory(iInventory), itemStack, sourceSlotTransaction); - } - - InventoryUtil.updateInventoryNoEvents(var3); - InventoryUtil.updateInventoryNoEvents(iInventory); - } - -} diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Neo_Tracker.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Neo_Tracker.java index 915e707ea5d..a2ed8becbe0 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Neo_Tracker.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Neo_Tracker.java @@ -63,7 +63,7 @@ public abstract class ServerPlayerGameModeMixin_Neo_Tracker { final ItemStack itemStack, final Level level, final BlockState state, final BlockPos pos, final Player player ) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); // Log the prepare drops here because Forge rewrites // this method to call mine block before calling destroy block diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/world/level/block/BlockMixin_Neo_Tracker.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/world/level/block/BlockMixin_Neo_Tracker.java index f9c274a8da3..1cb67b86be0 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/world/level/block/BlockMixin_Neo_Tracker.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/world/level/block/BlockMixin_Neo_Tracker.java @@ -25,15 +25,18 @@ package org.spongepowered.neoforge.mixin.tracker.world.level.block; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootTable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.bridge.RegistryBackedTrackableBridge; import org.spongepowered.neoforge.mixin.tracker.world.level.block.state.BlockBehaviorMixin_Neo_Tracker; +import java.util.Optional; + @Mixin(Block.class) public abstract class BlockMixin_Neo_Tracker extends BlockBehaviorMixin_Neo_Tracker implements RegistryBackedTrackableBridge { @@ -42,7 +45,7 @@ public abstract class BlockMixin_Neo_Tracker extends BlockBehaviorMixin_Neo_Trac //@formatter:on @Override - protected void forgeTracker$initializeTrackingState(CallbackInfoReturnable cir) { + protected void forgeTracker$initializeTrackingState(CallbackInfoReturnable>> cir) { // TODO Not the best check but the tracker options only matter during block ticks... if (this.isRandomlyTicking(this.defaultBlockState())) { this.bridge$refreshTrackerStates(); diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Neo_Tracker.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Neo_Tracker.java index 16ed44d3f87..116444b172f 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Neo_Tracker.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/tracker/world/level/block/state/BlockBehaviorMixin_Neo_Tracker.java @@ -25,15 +25,18 @@ package org.spongepowered.neoforge.mixin.tracker.world.level.block.state; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootTable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.Optional; + @Mixin(BlockBehaviour.class) public abstract class BlockBehaviorMixin_Neo_Tracker { @Shadow protected abstract boolean isRandomlyTicking(BlockState $$0); @@ -42,10 +45,10 @@ public abstract class BlockBehaviorMixin_Neo_Tracker { method = "getLootTable", at = @At( value = "FIELD", - target = "Lnet/minecraft/world/level/block/state/BlockBehaviour;lootTableSupplier:Ljava/util/function/Supplier;", + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour;drops:Ljava/util/Optional;", remap = false ) ) - protected void forgeTracker$initializeTrackingState(CallbackInfoReturnable cir) { + protected void forgeTracker$initializeTrackingState(CallbackInfoReturnable>> cir) { } } diff --git a/neoforge/src/mixins/resources/mixins.spongeneo.core.json b/neoforge/src/mixins/resources/mixins.spongeneo.core.json index e1852e0963c..43418efdf07 100644 --- a/neoforge/src/mixins/resources/mixins.spongeneo.core.json +++ b/neoforge/src/mixins/resources/mixins.spongeneo.core.json @@ -1,47 +1,55 @@ { - "required": true, - "parent": "mixins.sponge.parent.json", - "package": "org.spongepowered.neoforge.mixin.core", - "priority": 1301, - "mixins": [ - "api.event.EventMixin_Neo", - "api.event.block.ChangeBlockEvent_AllMixin_Neo", - "api.event.entity.ChangeEntityWorldEvent_PreMixin_Neo", - "api.event.entity.ChangeEventWorldEvent_PostMixin_Neo", - "neoforge.event.entity.item.ItemTossEventMixin_Neo", - "commands.CommandsMixin_Neo", - "neoforge.NeoForgeMixin_Neo", - "neoforge.event.entity.EntityTravelToDimensionEventMixin_Neo", - "neoforge.event.entity.player.PlayerEvent_PlayerChangedDimensionEventMixin_Neo", - "neoforge.event.world.BlockEvent_BreakEventMixin_Neo", - "neoforge.event.world.BlockEventMixin_Neo", - "neoforge.internal.BrandingControlMixin_Neo", - "neoforge.registries.GameDataMixin_Neo", - "neoforge.registries.RegistryManagerMixin_Neo", - "network.ConnectionMixin_Neo", - "network.protocol.common.ClientboundCustomPayloadPacketMixin_Neo", - "network.protocol.common.ServerboundCustomPayloadPacketMixin_Neo", - "server.BootstrapMixin_Neo", - "server.MinecraftServerMixin_Neo", - "server.commands.SpreadPlayersCommandMixin_Neo", - "server.level.ServerEntityMixin_Neo", - "server.level.ServerPlayerMixin_Neo", - "server.network.ServerGamePacketListenerImplMixin_Neo", - "tags.TagLoaderMixin_NeoForge", - "world.entity.LivingEntityMixin_Neo", - "world.entity.LivingEntityMixin_Neo_Attack_Impl", - "world.entity.item.ItemEntityMixin_Neo", - "world.entity.player.PlayerMixin_Neo_Attack_Impl", - "world.entity.projectile.FishingHookMixin_Neo", - "world.entity.vehicle.BoatMixin_Neo", - "world.level.block.FireBlockMixin_Neo", - "world.level.block.entity.AbstractFurnaceBlockEntityMixin_Neo" - ], - "client": [ - "client.MinecraftMixin_Neo", - "client.main.MainMixin_Neo" - ], - "server": [ - "server.MainMixin_Neo" + "required": true, + "parent": "mixins.sponge.parent.json", + "package": "org.spongepowered.neoforge.mixin.core", + "priority": 1301, + "injectors": { + "maxShiftBy": 3 + }, + "mixins": [ + "api.event.EventMixin_Neo", + "api.event.block.ChangeBlockEvent_AllMixin_Neo", + "api.event.entity.ChangeEntityWorldEvent_PreMixin_Neo", + "api.event.entity.ChangeEventWorldEvent_PostMixin_Neo", + "commands.CommandsMixin_Neo", + "neoforge.NeoForgeMixin_Neo", + "neoforge.event.entity.EntityTravelToDimensionEventMixin_Neo", + "neoforge.event.entity.item.ItemTossEventMixin_Neo", + "neoforge.event.entity.player.PlayerEvent_PlayerChangedDimensionEventMixin_Neo", + "neoforge.event.world.BlockEvent_BreakEventMixin_Neo", + "neoforge.event.world.BlockEventMixin_Neo", + "neoforge.internal.BrandingControlMixin_Neo", + "neoforge.registries.GameDataMixin_Neo", + "neoforge.registries.RegistryManagerMixin_Neo", + "network.ConnectionMixin_Neo", + "network.protocol.common.ClientboundCustomPayloadPacketMixin_Neo", + "network.protocol.common.ServerboundCustomPayloadPacketMixin_Neo", + "resources.RegistryDataLoaderMixin_Neo", + "server.BootstrapMixin_Neo", + "server.MinecraftServerMixin_Neo", + "server.ReloadableServerResourcesMixin_Neo", + "server.level.ServerPlayerMixin_Neo", + "server.network.ServerGamePacketListenerImplMixin_Neo", + "tags.TagLoaderMixin_NeoForge", + "world.entity.EntityMixin_Neo", + "world.entity.LivingEntityMixin_Neo", + "world.entity.LivingEntityMixin_Neo_Damage", + "world.entity.animal.golem.SnowGolemMixin_Neo", + "world.entity.item.ItemEntityMixin_Neo", + "world.entity.player.PlayerMixin_Neo_Damage", + "world.entity.projectile.FishingHookMixin_Neo", + "world.entity.vehicle.boat.AbstractBoatMixin_Neo", + "world.level.ServerExplosionMixin_NeoForge", + "world.item.TeleportRandomlyConsumeEffectMixin_Neo", + "world.item.crafting.RecipeManagerMixin_Neo", + "world.level.block.FireBlockMixin_Neo", + "world.level.block.entity.AbstractFurnaceBlockEntityMixin_Neo" + ], + "client": [ + "client.MinecraftMixin_Neo", + "client.main.MainMixin_Neo" + ], + "server": [ + "server.MainMixin_Neo" ] } diff --git a/neoforge/src/mixins/resources/mixins.spongeneo.inventory.json b/neoforge/src/mixins/resources/mixins.spongeneo.inventory.json index b42178d7290..3b57561e95e 100644 --- a/neoforge/src/mixins/resources/mixins.spongeneo.inventory.json +++ b/neoforge/src/mixins/resources/mixins.spongeneo.inventory.json @@ -6,7 +6,6 @@ "priority": 1300, "mixins": [ "event.entity.PlayerMixin_Inventory_Neo", - "event.server.level.ServerPlayerMixin_Inventory_Neo", - "event.world.level.block.entity.HopperBlockEntityMixin_Inventory_Neo" + "event.server.level.ServerPlayerMixin_Inventory_Neo" ] } diff --git a/neoforge/src/test/java/org/spongepowered/neoforge/boot/SpongeLauncherInterceptor.java b/neoforge/src/test/java/org/spongepowered/neoforge/boot/SpongeLauncherInterceptor.java index dcc5924a543..7d9338f4ae9 100644 --- a/neoforge/src/test/java/org/spongepowered/neoforge/boot/SpongeLauncherInterceptor.java +++ b/neoforge/src/test/java/org/spongepowered/neoforge/boot/SpongeLauncherInterceptor.java @@ -24,6 +24,7 @@ */ package org.spongepowered.neoforge.boot; +import net.neoforged.fml.loading.FMLLoader; import org.junit.platform.launcher.LauncherInterceptor; import org.spongepowered.common.applaunch.test.TestGameAccess; @@ -31,8 +32,6 @@ public final class SpongeLauncherInterceptor implements LauncherInterceptor { private final ClassLoader classLoader; - private long counter; - public SpongeLauncherInterceptor() { try { this.classLoader = SpongeTestBoot.getGameClassLoader(); @@ -43,10 +42,6 @@ public SpongeLauncherInterceptor() { @Override public T intercept(final Invocation invocation) { - // getResources broken on Neo - if (this.counter++ == 1) { - return invocation.proceed(); - } final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(this.classLoader); try { @@ -59,5 +54,6 @@ public T intercept(final Invocation invocation) { @Override public void close() { TestGameAccess.shutdownGame(); + FMLLoader.getCurrent().close(); } } diff --git a/neoforge/src/test/java/org/spongepowered/neoforge/boot/SpongeTestBoot.java b/neoforge/src/test/java/org/spongepowered/neoforge/boot/SpongeTestBoot.java index 416b5317e49..70a051fd0ea 100644 --- a/neoforge/src/test/java/org/spongepowered/neoforge/boot/SpongeTestBoot.java +++ b/neoforge/src/test/java/org/spongepowered/neoforge/boot/SpongeTestBoot.java @@ -24,7 +24,7 @@ */ package org.spongepowered.neoforge.boot; -import org.spongepowered.bootstrap.neoforge.NeoForgeBootstrap; +import org.spongepowered.bootstrap.neoforge.TestServer; import org.spongepowered.common.applaunch.test.TestGameAccess; import java.nio.file.Files; @@ -39,7 +39,7 @@ public static ClassLoader getGameClassLoader() throws Exception { .skip(1) // main class .toArray(String[]::new); System.setProperty("sponge.test.active", "true"); - new NeoForgeBootstrap(args).devBoot(false); + TestServer.main(args); } return TestGameAccess.getGameClassLoader(); } diff --git a/settings.gradle.kts b/settings.gradle.kts index 7bfce3fb041..fe4b70e7155 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,7 +10,7 @@ pluginManagement { } } plugins { - id("org.spongepowered.gradle.vanilla") version "0.2.2" + id("org.spongepowered.gradle.vanilla") version "0.3.2" id("implementation-structure") } } @@ -22,6 +22,19 @@ plugins { dependencyResolutionManagement { repositoriesMode = RepositoriesMode.PREFER_PROJECT // needed for ForgeGradle, unfortunately + repositories { + mavenCentral() { + content { + excludeGroup("org.lwjgl") + } + } + maven("https://libraries.minecraft.net/") { + name = "mojang" + content { + includeGroup("org.lwjgl") + } + } + } repositories { maven("https://repo.spongepowered.org/repository/maven-public/") { name = "sponge" @@ -62,7 +75,6 @@ includeBuild("SpongeAPI") { substitute(module("org.spongepowered:spongeapi")).using(project(":")) } } -include("modlauncher-transformers") include("library-manager") include("bootstrap") include("generator") diff --git a/src/accessors/java/org/spongepowered/common/accessor/advancements/CriteriaTriggersAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/advancements/CriteriaTriggersAccessor.java index d0a55751335..2236ca8c735 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/advancements/CriteriaTriggersAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/advancements/CriteriaTriggersAccessor.java @@ -24,8 +24,8 @@ */ package org.spongepowered.common.accessor.advancements; -import net.minecraft.advancements.CriteriaTriggers; -import net.minecraft.advancements.CriterionTrigger; +import net.minecraft.advancements.triggers.CriteriaTriggers; +import net.minecraft.advancements.triggers.CriterionTrigger; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.common.UntransformedInvokerError; diff --git a/src/accessors/java/org/spongepowered/common/accessor/client/multiplayer/ClientLevelAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/client/multiplayer/ClientLevelAccessor.java index d588cd5ff60..e1cea955966 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/client/multiplayer/ClientLevelAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/client/multiplayer/ClientLevelAccessor.java @@ -25,11 +25,9 @@ package org.spongepowered.common.accessor.client.multiplayer; import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.DimensionSpecialEffects; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.entity.TransientEntitySectionManager; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(ClientLevel.class) @@ -37,5 +35,4 @@ public interface ClientLevelAccessor { @Accessor("entityStorage") TransientEntitySectionManager accessor$getEntityStorage(); - @Mutable @Accessor("effects") void accessor$effects(DimensionSpecialEffects effects); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/commands/CommandSourceStackAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/commands/CommandSourceStackAccessor.java index f138e5b446f..c4ad6b45358 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/commands/CommandSourceStackAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/commands/CommandSourceStackAccessor.java @@ -32,6 +32,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.permissions.PermissionSet; import net.minecraft.util.TaskChainer; import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.Vec2; @@ -50,7 +51,7 @@ public interface CommandSourceStackAccessor { final Vec3 $$1, final Vec2 $$2, final ServerLevel $$3, - final int $$4, + final PermissionSet $$4, final String $$5, final Component $$6, final MinecraftServer $$7, @Nullable final Entity $$8, diff --git a/src/accessors/java/org/spongepowered/common/accessor/entity/passive/AbstractChestedHorseEntityAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/entity/animal/equine/AbstractChestedHorseEntityAccessor.java similarity index 92% rename from src/accessors/java/org/spongepowered/common/accessor/entity/passive/AbstractChestedHorseEntityAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/entity/animal/equine/AbstractChestedHorseEntityAccessor.java index da4386c2b96..45d68eaf136 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/entity/passive/AbstractChestedHorseEntityAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/entity/animal/equine/AbstractChestedHorseEntityAccessor.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.entity.passive; +package org.spongepowered.common.accessor.entity.animal.equine; import net.minecraft.network.syncher.EntityDataAccessor; -import net.minecraft.world.entity.animal.horse.AbstractChestedHorse; +import net.minecraft.world.entity.animal.equine.AbstractChestedHorse; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.common.UntransformedAccessorError; diff --git a/src/accessors/java/org/spongepowered/common/accessor/entity/animal/frog/FrogAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/entity/animal/frog/FrogAccessor.java new file mode 100644 index 00000000000..00dc414e649 --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/entity/animal/frog/FrogAccessor.java @@ -0,0 +1,37 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.entity.animal.frog; + +import net.minecraft.core.Holder; +import net.minecraft.world.entity.animal.frog.Frog; +import net.minecraft.world.entity.animal.frog.FrogVariant; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(Frog.class) +public interface FrogAccessor { + + @Invoker("setVariant") void invoker$setVariant(Holder variant); +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/advancements/critereon/MinMaxBounds_IntsAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/entity/vehicle/minecart/AbstractMinecartAccessor.java similarity index 76% rename from src/accessors/java/org/spongepowered/common/accessor/advancements/critereon/MinMaxBounds_IntsAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/entity/vehicle/minecart/AbstractMinecartAccessor.java index a931c5acca8..a95b583c59f 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/advancements/critereon/MinMaxBounds_IntsAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/entity/vehicle/minecart/AbstractMinecartAccessor.java @@ -22,20 +22,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.advancements.critereon; +package org.spongepowered.common.accessor.entity.vehicle.minecart; -import net.minecraft.advancements.critereon.MinMaxBounds; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecart; +import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; -import org.spongepowered.common.UntransformedInvokerError; import java.util.Optional; -@Mixin(MinMaxBounds.Ints.class) -public interface MinMaxBounds_IntsAccessor { +@Mixin(AbstractMinecart.class) +public interface AbstractMinecartAccessor { - @Invoker("") static MinMaxBounds.Ints invoker$new(final Optional min, final Optional max) { - throw new UntransformedInvokerError(); - } + @Invoker("getCustomDisplayBlockState") Optional accessor$getCustomDisplayBlockState(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/network/chat/HoverEvent_ItemStackInfoAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/network/chat/HoverEvent_ItemStackInfoAccessor.java deleted file mode 100644 index 14c89652187..00000000000 --- a/src/accessors/java/org/spongepowered/common/accessor/network/chat/HoverEvent_ItemStackInfoAccessor.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.accessor.network.chat; - -import net.minecraft.core.Holder; -import net.minecraft.core.component.DataComponentPatch; -import net.minecraft.network.chat.HoverEvent; -import net.minecraft.world.item.Item; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; -import org.spongepowered.common.UntransformedInvokerError; - -@Mixin(HoverEvent.ItemStackInfo.class) -public interface HoverEvent_ItemStackInfoAccessor { - - @Invoker("") - static HoverEvent.ItemStackInfo invoker$new(final Holder item, final int count, final DataComponentPatch patch) { - throw new UntransformedInvokerError(); - } -} diff --git a/src/accessors/java/org/spongepowered/common/accessor/network/chat/StyleAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/network/chat/StyleAccessor.java index ede074bb8f8..d79197232d8 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/network/chat/StyleAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/network/chat/StyleAccessor.java @@ -25,37 +25,53 @@ package org.spongepowered.common.accessor.network.chat; import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.FontDescription; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.Style; import net.minecraft.network.chat.TextColor; -import net.minecraft.resources.ResourceLocation; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.common.UntransformedInvokerError; +import javax.annotation.Nullable; + @Mixin(Style.class) public interface StyleAccessor { @Invoker("") - static Style invoker$new(final @Nullable TextColor color, final @Nullable Boolean bold, final @Nullable Boolean italic, - final @Nullable Boolean underlined, final @Nullable Boolean strikethrough, final @Nullable Boolean obfuscated, - final @Nullable ClickEvent clickEvent, final @Nullable HoverEvent hoverEvent, final @Nullable String insertion, - final @Nullable ResourceLocation font) { + static Style invoker$new( + final @Nullable TextColor color, + final @Nullable Integer shadowColor, + final @Nullable Boolean bold, + final @Nullable Boolean italic, + final @Nullable Boolean underlined, + final @Nullable Boolean strikethrough, + final @Nullable Boolean obfuscated, + final @Nullable ClickEvent clickEvent, + final @Nullable HoverEvent hoverEvent, + final @Nullable String insertion, + final @Nullable FontDescription fontDescription + ) { throw new UntransformedInvokerError(); } - @Accessor("bold") Boolean accessor$bold(); + @Accessor("bold") + Boolean accessor$bold(); - @Accessor("italic") Boolean accessor$italic(); + @Accessor("italic") + Boolean accessor$italic(); - @Accessor("underlined") Boolean accessor$underlined(); + @Accessor("underlined") + Boolean accessor$underlined(); - @Accessor("strikethrough") Boolean accessor$strikethrough(); + @Accessor("strikethrough") + Boolean accessor$strikethrough(); - @Accessor("obfuscated") Boolean accessor$obfuscated(); + @Accessor("obfuscated") + Boolean accessor$obfuscated(); - @Accessor("font") ResourceLocation accessor$font(); + @Accessor("font") + FontDescription accessor$font(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundMoveVehiclePacketAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundMoveVehiclePacketAccessor.java index c506173e7d3..a0865d608f4 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundMoveVehiclePacketAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundMoveVehiclePacketAccessor.java @@ -25,6 +25,7 @@ package org.spongepowered.common.accessor.network.protocol.game; import net.minecraft.network.protocol.game.ServerboundMoveVehiclePacket; +import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; @@ -32,11 +33,7 @@ @Mixin(ServerboundMoveVehiclePacket.class) public interface ServerboundMoveVehiclePacketAccessor { - @Accessor("x") @Mutable void accessor$x(final double x); - - @Accessor("y") @Mutable void accessor$y(final double y); - - @Accessor("z") @Mutable void accessor$z(final double z); + @Accessor("position") @Mutable void accessor$position(final Vec3 position); @Accessor("yRot") @Mutable void accessor$yRot(final float yRot); diff --git a/src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundInteractPacket_InteractionAtLocationActionAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/resources/RegistryDataLoader_ResourceManagerRegistryLoadTaskAccessor.java similarity index 77% rename from src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundInteractPacket_InteractionAtLocationActionAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/resources/RegistryDataLoader_ResourceManagerRegistryLoadTaskAccessor.java index 16b27acb95a..f5a2981f554 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundInteractPacket_InteractionAtLocationActionAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/resources/RegistryDataLoader_ResourceManagerRegistryLoadTaskAccessor.java @@ -22,15 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.network.protocol.game; +package org.spongepowered.common.accessor.resources; -import net.minecraft.world.InteractionHand; +import net.minecraft.resources.ResourceManagerRegistryLoadTask; +import net.minecraft.server.packs.resources.ResourceManager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(targets = "net.minecraft.network.protocol.game.ServerboundInteractPacket$InteractionAtLocationAction") -public interface ServerboundInteractPacket_InteractionAtLocationActionAccessor { - - @Accessor("hand") InteractionHand accessor$hand(); +@Mixin(ResourceManagerRegistryLoadTask.class) +public interface RegistryDataLoader_ResourceManagerRegistryLoadTaskAccessor { + @Accessor("resourceManager") ResourceManager accessor$getResourceManager(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/resources/RegistryLoadTaskAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/resources/RegistryLoadTaskAccessor.java new file mode 100644 index 00000000000..536d38c1ad5 --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/resources/RegistryLoadTaskAccessor.java @@ -0,0 +1,39 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.resources; + +import net.minecraft.core.WritableRegistry; +import net.minecraft.resources.RegistryDataLoader; +import net.minecraft.resources.RegistryLoadTask; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(RegistryLoadTask.class) +public interface RegistryLoadTaskAccessor { + + @Accessor("data") RegistryDataLoader.RegistryData accessor$data(); + + @Accessor("registry") WritableRegistry accessor$registry(); +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/resources/ResourceKeyAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/resources/ResourceKeyAccessor.java index cf288ba96a5..999584f968d 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/resources/ResourceKeyAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/resources/ResourceKeyAccessor.java @@ -24,8 +24,8 @@ */ package org.spongepowered.common.accessor.resources; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; @@ -34,9 +34,9 @@ @Mixin(ResourceKey.class) public interface ResourceKeyAccessor { - @Invoker("create") static ResourceKey invoker$create(final ResourceLocation p_240905_0_, final ResourceLocation p_240905_1_) { + @Invoker("create") static ResourceKey invoker$create(final Identifier p_240905_0_, final Identifier p_240905_1_) { throw new UntransformedInvokerError(); } - @Accessor("registryName") ResourceLocation accessor$registryName(); + @Accessor("registryName") Identifier accessor$registryName(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/server/MinecraftServerAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/server/MinecraftServerAccessor.java index e77ac22e1d5..cb25acec6a2 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/server/MinecraftServerAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/server/MinecraftServerAccessor.java @@ -27,7 +27,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.progress.ChunkProgressListenerFactory; +import net.minecraft.server.level.progress.LevelLoadListener; import net.minecraft.world.level.Level; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.ServerLevelData; @@ -49,7 +49,7 @@ public interface MinecraftServerAccessor { throw new UntransformedAccessorError(); } - @Invoker("setInitialSpawn") static void invoker$setInitialSpawn(final ServerLevel serverWorld, final ServerLevelData levelData, final boolean generateBonusChest, final boolean debugWorld) { + @Invoker("setInitialSpawn") static void invoker$setInitialSpawn(final ServerLevel serverWorld, final ServerLevelData levelData, final boolean generateBonusChest, final boolean debugWorld, LevelLoadListener listener) { throw new UntransformedInvokerError(); } @@ -59,17 +59,11 @@ public interface MinecraftServerAccessor { @Accessor("executor") Executor accessor$executor(); - @Accessor("progressListenerFactory") ChunkProgressListenerFactory accessor$progressListenerFactory(); - @Accessor("nextTickTimeNanos") void accessor$nextTickTimeNanos(final long nextTickTime); - @Invoker("isSpawningMonsters") boolean invoker$isSpawningMonsters(); - @Invoker("setupDebugLevel") void invoker$setupDebugLevel(final WorldData serverConfiguration); @Invoker("forceDifficulty") void invoker$forceDifficulty(); @Invoker("waitUntilNextTick") void accessor$waitUntilNextTick(); - - @Invoker("synchronizeTime") void invoker$synchronizeTime(ServerLevel level); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/server/ServerFunctionManagerAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/server/MinecraftServer_ReloadableResourcesAccessor.java similarity index 82% rename from src/accessors/java/org/spongepowered/common/accessor/server/ServerFunctionManagerAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/server/MinecraftServer_ReloadableResourcesAccessor.java index 17a152c5425..a2415a92606 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/server/ServerFunctionManagerAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/server/MinecraftServer_ReloadableResourcesAccessor.java @@ -24,13 +24,12 @@ */ package org.spongepowered.common.accessor.server; -import net.minecraft.server.ServerFunctionLibrary; -import net.minecraft.server.ServerFunctionManager; +import net.minecraft.server.packs.resources.CloseableResourceManager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(ServerFunctionManager.class) -public interface ServerFunctionManagerAccessor { +@Mixin(targets = "net.minecraft.server.MinecraftServer$ReloadableResources") +public interface MinecraftServer_ReloadableResourcesAccessor { - @Accessor("library") ServerFunctionLibrary accessor$library(); + @Accessor("resourceManager") CloseableResourceManager accessor$resourceManager(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/EntityTypeAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/server/bossevents/CustomBossEventsAccessor.java similarity index 78% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/EntityTypeAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/server/bossevents/CustomBossEventsAccessor.java index 8ef9cbcdcb8..b4d7bb53c3d 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/EntityTypeAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/server/bossevents/CustomBossEventsAccessor.java @@ -22,20 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity; +package org.spongepowered.common.accessor.server.bossevents; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntityType; +import com.mojang.serialization.Codec; +import net.minecraft.server.bossevents.CustomBossEvents; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; +import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.common.UntransformedAccessorError; -@Mixin(EntityType.class) -public interface EntityTypeAccessor { - - @Invoker("register") - static EntityType invoker$register(final String key, final EntityType.Builder builder) { +@Mixin(CustomBossEvents.class) +public interface CustomBossEventsAccessor { + @Accessor("CODEC") static Codec accessor$codec() { throw new UntransformedAccessorError(); } - } diff --git a/src/accessors/java/org/spongepowered/common/accessor/server/level/ChunkMapAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/server/level/ChunkMapAccessor.java index c24a269be3e..5a0dda5ac37 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/server/level/ChunkMapAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/server/level/ChunkMapAccessor.java @@ -43,7 +43,7 @@ public interface ChunkMapAccessor { @Invoker("saveAllChunks") void invoker$saveAllChunks(boolean flush); - @Invoker("getChunks") Iterable invoker$getChunks(); + @Accessor("visibleChunkMap") Long2ObjectLinkedOpenHashMap accessor$visibleChunkMap(); @Accessor("level") ServerLevel accessor$level(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/server/level/ServerLevelAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/server/level/ServerLevelAccessor.java index 52b13a0586e..092b199e59f 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/server/level/ServerLevelAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/server/level/ServerLevelAccessor.java @@ -28,12 +28,15 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.BlockEventData; +import net.minecraft.world.level.CustomSpawner; import net.minecraft.world.level.entity.PersistentEntitySectionManager; import org.slf4j.Logger; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.common.UntransformedAccessorError; +import java.util.List; + @Mixin(ServerLevel.class) public interface ServerLevelAccessor { @@ -44,10 +47,8 @@ public interface ServerLevelAccessor { @Accessor("blockEvents") ObjectLinkedOpenHashSet accessor$blockEvents(); - @Accessor("entityManager") PersistentEntitySectionManager accessor$getEntityManager(); - - @Accessor("lastSpawnChunkRadius") int accessor$lastSpawnChunkRadius(); + @Accessor("customSpawners") List accessor$customSpawners(); - @Accessor("lastSpawnChunkRadius") void accessor$setLastSpawnChunkRadius(int value); + @Accessor("entityManager") PersistentEntitySectionManager accessor$getEntityManager(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/server/level/ServerPlayerAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/server/level/ServerPlayerAccessor.java index d99f6209cce..a2153f86dba 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/server/level/ServerPlayerAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/server/level/ServerPlayerAccessor.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.accessor.server.level; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import org.spongepowered.asm.mixin.Mixin; @@ -48,4 +49,6 @@ public interface ServerPlayerAccessor { @Invoker("triggerDimensionChangeTriggers") void invoker$triggerDimensionChangeTriggers(final ServerLevel world); @Accessor("containerCounter") int accessor$containerCounter(); + + @Accessor("server") MinecraftServer accessor$server(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/server/level/TicketAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/server/level/TicketAccessor.java index 02b8f67d302..e185ca3443a 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/server/level/TicketAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/server/level/TicketAccessor.java @@ -25,26 +25,12 @@ package org.spongepowered.common.accessor.server.level; import net.minecraft.server.level.Ticket; -import net.minecraft.server.level.TicketType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -import org.spongepowered.asm.mixin.gen.Invoker; -import org.spongepowered.common.UntransformedInvokerError; @Mixin(Ticket.class) -public interface TicketAccessor { +public interface TicketAccessor { - @Invoker("") - static Ticket accessor$createInstance(final TicketType ticketType, final int ticketLevel, final T key) { - throw new UntransformedInvokerError(); - } - - @Accessor("createdTick") long accessor$createdTick(); - - @Accessor("key") T accessor$key(); - - @Invoker("timedOut") boolean invoker$timedOut(long currentTimestamp); - - @Invoker("setCreatedTick") void invoker$setCreatedTick(long timestamp); + @Accessor("ticksLeft") long accessor$ticksLeft(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/server/players/GameProfileCache_GameProfileInfoAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/server/players/GameProfileCache_GameProfileInfoAccessor.java index e197f0ca804..7dac013db5f 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/server/players/GameProfileCache_GameProfileInfoAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/server/players/GameProfileCache_GameProfileInfoAccessor.java @@ -24,18 +24,18 @@ */ package org.spongepowered.common.accessor.server.players; -import com.mojang.authlib.GameProfile; +import net.minecraft.server.players.NameAndId; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; import java.util.Date; -@Mixin(targets = "net.minecraft.server.players.GameProfileCache$GameProfileInfo") +@Mixin(targets = "net.minecraft.server.players.CachedUserNameToIdResolver$GameProfileInfo") public interface GameProfileCache_GameProfileInfoAccessor { - @Invoker("getProfile") GameProfile invoker$getProfile(); + @Invoker("nameAndId") NameAndId invoker$nameAndId(); - @Invoker("getExpirationDate") Date invoker$getExpirationDate(); + @Invoker("expirationDate") Date invoker$getExpirationDate(); @Invoker("setLastAccess") void invoker$setLastAccess(final long lastAccessMs); diff --git a/src/accessors/java/org/spongepowered/common/accessor/sounds/SoundEventAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/sounds/SoundEventAccessor.java index 35ebdd5a590..1ca68b206a6 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/sounds/SoundEventAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/sounds/SoundEventAccessor.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.accessor.sounds; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.sounds.SoundEvent; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; @@ -34,7 +34,7 @@ public interface SoundEventAccessor { @Invoker("createVariableRangeEvent") - static SoundEvent accessor$createVariableRangeEvent(ResourceLocation $$0) { + static SoundEvent accessor$createVariableRangeEvent(Identifier $$0) { throw new UntransformedAccessorError(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/tags/TagEntryAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/tags/TagEntryAccessor.java index f4b86e2eea8..fbe72f7628d 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/tags/TagEntryAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/tags/TagEntryAccessor.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.accessor.tags; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.tags.TagEntry; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @@ -32,7 +32,7 @@ @Mixin(TagEntry.class) public interface TagEntryAccessor { - @Accessor("id") ResourceLocation accessor$id(); + @Accessor("id") Identifier accessor$id(); @Accessor("tag") boolean accessor$tag(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/SlimeAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/util/BlockableEventLoopAccessor.java similarity index 84% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/SlimeAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/util/BlockableEventLoopAccessor.java index 22edef12b4b..b550217ac8b 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/SlimeAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/util/BlockableEventLoopAccessor.java @@ -22,15 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.monster; +package org.spongepowered.common.accessor.util; -import net.minecraft.world.entity.monster.Slime; +import net.minecraft.util.thread.BlockableEventLoop; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; -@Mixin(Slime.class) -public interface SlimeAccessor { +@Mixin(BlockableEventLoop.class) +public interface BlockableEventLoopAccessor { - @Invoker("setSize") void invoker$setSize(final int size, final boolean resetHealth); + @Invoker("pollTask") boolean invoker$pollTask(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/util/datafix/schemas/V100Accessor.java b/src/accessors/java/org/spongepowered/common/accessor/util/datafix/schemas/V100Accessor.java index 86f40439adb..87a31ea054c 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/util/datafix/schemas/V100Accessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/util/datafix/schemas/V100Accessor.java @@ -34,7 +34,7 @@ @Mixin(V100.class) public interface V100Accessor { - @Invoker("equipment") + @Invoker("lambda$registerTypes$0") static TypeTemplate invoker$equipment(final Schema schema) { throw new UntransformedInvokerError(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/DamageSourcesAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/DamageSourcesAccessor.java new file mode 100644 index 00000000000..c4d1f14afef --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/world/DamageSourcesAccessor.java @@ -0,0 +1,37 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.world; + +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.damagesource.DamageSources; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(DamageSources.class) +public interface DamageSourcesAccessor { + + @Accessor("inFire") @Mutable void accessor$setInFire(DamageSource source); +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/PigAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/AvatarAccessor.java similarity index 77% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/PigAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/AvatarAccessor.java index 86fc53175d3..783a850d405 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/PigAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/AvatarAccessor.java @@ -22,21 +22,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal; +package org.spongepowered.common.accessor.world.entity; import net.minecraft.network.syncher.EntityDataAccessor; -import net.minecraft.world.entity.ItemBasedSteering; -import net.minecraft.world.entity.animal.Pig; +import net.minecraft.world.entity.Avatar; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.common.UntransformedAccessorError; -@Mixin(Pig.class) -public interface PigAccessor { +@Mixin(Avatar.class) +public interface AvatarAccessor { - @Accessor("DATA_SADDLE_ID") static EntityDataAccessor accessor$DATA_SADDLE_ID() { + @Accessor("DATA_PLAYER_MODE_CUSTOMISATION") static EntityDataAccessor accessor$DATA_PLAYER_MODE_CUSTOMISATION() { + throw new UntransformedAccessorError(); + } + + @Accessor("DATA_PLAYER_MAIN_HAND") static EntityDataAccessor accessor$DATA_PLAYER_MAIN_HAND() { throw new UntransformedAccessorError(); } - @Accessor("steering") ItemBasedSteering accessor$steering(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/EntityAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/EntityAccessor.java index 538bfb02607..8a3171168a1 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/EntityAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/EntityAccessor.java @@ -87,8 +87,6 @@ public interface EntityAccessor { @Invoker("getFireImmuneTicks") int invoker$getFireImmuneTicks(); - @Invoker("getPermissionLevel") int invoker$getPermissionLevel(); - @Invoker("unsetRemoved") void invoker$unsetRemoved(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/ExperienceOrbAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/ExperienceOrbAccessor.java index c1e51d24e25..a923dcf2443 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/ExperienceOrbAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/ExperienceOrbAccessor.java @@ -26,13 +26,13 @@ import net.minecraft.world.entity.ExperienceOrb; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; @Mixin(ExperienceOrb.class) public interface ExperienceOrbAccessor { - @Accessor("value") int accessor$value(); + @Invoker("getValue") int accessor$value(); - @Accessor("value") void accessor$value(final int value); + @Invoker("setValue") void accessor$value(final int value); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/LivingEntityAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/LivingEntityAccessor.java index 4c6ed062320..55e5ae7a68a 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/LivingEntityAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/LivingEntityAccessor.java @@ -27,10 +27,8 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.network.syncher.EntityDataAccessor; -import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; @@ -77,14 +75,10 @@ public interface LivingEntityAccessor { throw new UntransformedAccessorError(); } - @Accessor("dead") boolean accessor$dead(); - @Accessor("lastHurt") float accessor$lastHurt(); @Accessor("lastHurt") void accessor$lastHurt(final float lastHurt); - @Accessor("lastHurtByMob") @Nullable LivingEntity accessor$lastHurtByMob(); - @Accessor("autoSpinAttackTicks") int accessor$autoSpinAttackTicks(); @Accessor("autoSpinAttackTicks") void accessor$autoSpinAttackTicks(final int autoSpinAttackTicks); @Accessor("autoSpinAttackDmg") float accessor$autoSpinAttackDmg(); @@ -94,6 +88,4 @@ public interface LivingEntityAccessor { @Accessor("useItem") void accessor$useItem(final ItemStack useItem); - @Invoker("isDamageSourceBlocked") boolean invoker$isDamageSourceBlocked(final DamageSource source); - } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/ai/targeting/TargetingConditionsAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/ai/targeting/TargetingConditionsAccessor.java index e975fb1e014..7301d5c9c4c 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/ai/targeting/TargetingConditionsAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/ai/targeting/TargetingConditionsAccessor.java @@ -24,16 +24,14 @@ */ package org.spongepowered.common.accessor.world.entity.ai.targeting; -import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.targeting.TargetingConditions; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -import java.util.function.Predicate; @Mixin(TargetingConditions.class) public interface TargetingConditionsAccessor { - @Accessor("selector") Predicate accessor$selector(); + @Accessor("selector") TargetingConditions.Selector accessor$selector(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/AnimalAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/AnimalAccessor.java index 2eeacc7a4ef..0e9cc08ac3d 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/AnimalAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/AnimalAccessor.java @@ -24,17 +24,18 @@ */ package org.spongepowered.common.accessor.world.entity.animal; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.EntityReference; import net.minecraft.world.entity.animal.Animal; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -import java.util.UUID; @Mixin(Animal.class) public interface AnimalAccessor { - @Accessor("loveCause") UUID accessor$loveCause(); + @Accessor("loveCause") EntityReference accessor$loveCause(); - @Accessor("loveCause") void accessor$loveCause(final UUID loveCause); + @Accessor("loveCause") void accessor$loveCause(final EntityReference loveCause); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/nbt/CompoundTagAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/cow/MooshroomAccessor.java similarity index 81% rename from src/accessors/java/org/spongepowered/common/accessor/nbt/CompoundTagAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/cow/MooshroomAccessor.java index 4e37e636584..2a0c87e9d7f 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/nbt/CompoundTagAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/cow/MooshroomAccessor.java @@ -22,18 +22,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.nbt; +package org.spongepowered.common.accessor.world.entity.animal.cow; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; +import net.minecraft.world.entity.animal.cow.MushroomCow; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; -import java.util.Map; -import java.util.Set; +@Mixin(MushroomCow.class) +public interface MooshroomAccessor { -@Mixin(CompoundTag.class) -public interface CompoundTagAccessor { - - @Invoker("entrySet") Set> invoker$entrySet(); + @Invoker("setVariant") void invoker$setVariant(MushroomCow.Variant type); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/AbstractHorseAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/AbstractHorseAccessor.java new file mode 100644 index 00000000000..1d66b24c54b --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/AbstractHorseAccessor.java @@ -0,0 +1,38 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.world.entity.animal.equine; + +import net.minecraft.world.entity.EntityReference; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.animal.equine.AbstractHorse; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(AbstractHorse.class) +public interface AbstractHorseAccessor { + + @Accessor("owner") void accessor$setOwner(EntityReference owner); + +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/horse/HorseAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/HorseAccessor.java similarity index 92% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/horse/HorseAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/HorseAccessor.java index 3e51d825acc..44c8194f034 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/horse/HorseAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/HorseAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal.horse; +package org.spongepowered.common.accessor.world.entity.animal.equine; -import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.equine.Horse; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/horse/LlamaAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/LlamaAccessor.java similarity index 88% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/horse/LlamaAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/LlamaAccessor.java index 6f27f6078cc..5b00f50fa55 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/horse/LlamaAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/LlamaAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal.horse; +package org.spongepowered.common.accessor.world.entity.animal.equine; -import net.minecraft.world.entity.animal.horse.Llama; +import net.minecraft.world.entity.animal.equine.Llama; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; @@ -33,4 +33,5 @@ public interface LlamaAccessor { @Invoker("setStrength") void invoker$setStrength(final int strength); + @Invoker("setVariant") void invoker$setVariant(Llama.Variant v); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/horse/TraderLlamaAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/TraderLlamaAccessor.java similarity index 92% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/horse/TraderLlamaAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/TraderLlamaAccessor.java index 68de982a58d..52bbd33c7cc 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/horse/TraderLlamaAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/equine/TraderLlamaAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal.horse; +package org.spongepowered.common.accessor.world.entity.animal.equine; -import net.minecraft.world.entity.animal.horse.TraderLlama; +import net.minecraft.world.entity.animal.equine.TraderLlama; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/CatAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/feline/CatAccessor.java similarity index 84% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/CatAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/feline/CatAccessor.java index 0612395f449..25a48474b53 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/CatAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/feline/CatAccessor.java @@ -22,9 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal; +package org.spongepowered.common.accessor.world.entity.animal.feline; -import net.minecraft.world.entity.animal.Cat; +import net.minecraft.core.Holder; +import net.minecraft.world.entity.animal.feline.Cat; +import net.minecraft.world.entity.animal.feline.CatVariant; import net.minecraft.world.item.DyeColor; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; @@ -37,4 +39,7 @@ public interface CatAccessor { @Invoker("setRelaxStateOne") void invoker$setRelaxStateOne(boolean $$0); @Invoker("isRelaxStateOne") boolean invoker$isRelaxStateOne(); + + @Invoker("setVariant") void invoker$setVariant(Holder $$0); + } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/OcelotAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/feline/OcelotAccessor.java similarity index 92% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/OcelotAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/feline/OcelotAccessor.java index 79c98363632..1ede97ac4d1 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/OcelotAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/feline/OcelotAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal; +package org.spongepowered.common.accessor.world.entity.animal.feline; -import net.minecraft.world.entity.animal.Ocelot; +import net.minecraft.world.entity.animal.feline.Ocelot; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/PufferfishAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/fish/PufferfishAccessor.java similarity index 92% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/PufferfishAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/fish/PufferfishAccessor.java index e3ae8be5138..5060c719199 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/PufferfishAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/fish/PufferfishAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal; +package org.spongepowered.common.accessor.world.entity.animal.fish; -import net.minecraft.world.entity.animal.Pufferfish; +import net.minecraft.world.entity.animal.fish.Pufferfish; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.common.UntransformedInvokerError; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/TropicalFishAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/fish/TropicalFishAccessor.java similarity index 83% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/TropicalFishAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/fish/TropicalFishAccessor.java index 5ba9a299527..13f3b955bb4 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/TropicalFishAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/fish/TropicalFishAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal; +package org.spongepowered.common.accessor.world.entity.animal.fish; -import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.entity.animal.fish.TropicalFish; import net.minecraft.world.item.DyeColor; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; @@ -41,5 +41,10 @@ public interface TropicalFishAccessor { @Invoker("setPackedVariant") void invoker$setPackedVariant(final int packedVariant); + @Invoker("setPattern") void invoker$setPattern(final TropicalFish.Pattern v); + + @Invoker("setBaseColor") void invoker$setBaseColor(DyeColor v); + + @Invoker("setPatternColor") void invoker$setPatternColor(DyeColor v); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/FoxAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/fox/FoxAccessor.java similarity index 83% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/FoxAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/fox/FoxAccessor.java index ed8de32ec73..140de226c25 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/FoxAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/fox/FoxAccessor.java @@ -22,26 +22,27 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal; +package org.spongepowered.common.accessor.world.entity.animal.fox; import net.minecraft.network.syncher.EntityDataAccessor; -import net.minecraft.world.entity.animal.Fox; +import net.minecraft.world.entity.EntityReference; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.animal.fox.Fox; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.common.UntransformedAccessorError; import java.util.Optional; -import java.util.UUID; @Mixin(Fox.class) public interface FoxAccessor { - @Accessor("DATA_TRUSTED_ID_0") static EntityDataAccessor> accessor$DATA_TRUSTED_ID_0() { + @Accessor("DATA_TRUSTED_ID_0") static EntityDataAccessor>> accessor$DATA_TRUSTED_ID_0() { throw new UntransformedAccessorError(); } - @Accessor("DATA_TRUSTED_ID_1") static EntityDataAccessor> accessor$DATA_TRUSTED_ID_1() { + @Accessor("DATA_TRUSTED_ID_1") static EntityDataAccessor>> accessor$DATA_TRUSTED_ID_1() { throw new UntransformedAccessorError(); } @@ -53,4 +54,6 @@ public interface FoxAccessor { @Invoker("setSleeping") void invoker$setSleeping(final boolean sleeping); + @Invoker("setVariant") void invoker$setVariant(Fox.Variant v); + } diff --git a/src/accessors/java/org/spongepowered/common/accessor/ChatFormattingAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/frog/TadpoleAccessor.java similarity index 82% rename from src/accessors/java/org/spongepowered/common/accessor/ChatFormattingAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/frog/TadpoleAccessor.java index db438fb6508..9aeb7fcb798 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/ChatFormattingAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/frog/TadpoleAccessor.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor; +package org.spongepowered.common.accessor.world.entity.animal.frog; -import net.minecraft.ChatFormatting; -import org.checkerframework.checker.nullness.qual.Nullable; +import net.minecraft.world.entity.animal.frog.Tadpole; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(ChatFormatting.class) -public interface ChatFormattingAccessor { +@Mixin(Tadpole.class) +public interface TadpoleAccessor { - @Accessor("color") @Nullable Integer accessor$color(); + @Accessor("age") int accessor$getAge(); + + @Accessor("age") void accessor$setAge(int age); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/PandaAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/panda/PandaAccessor.java similarity index 92% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/PandaAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/panda/PandaAccessor.java index 108632523f4..399206e5794 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/PandaAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/panda/PandaAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal; +package org.spongepowered.common.accessor.world.entity.animal.panda; -import net.minecraft.world.entity.animal.Panda; +import net.minecraft.world.entity.animal.panda.Panda; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/parrot/ParrotAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/parrot/ParrotAccessor.java new file mode 100644 index 00000000000..a22b68a2e17 --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/parrot/ParrotAccessor.java @@ -0,0 +1,35 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.world.entity.animal.parrot; + +import net.minecraft.world.entity.animal.parrot.Parrot; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(Parrot.class) +public interface ParrotAccessor { + + @Invoker("setVariant") void invoker$setVariant(Parrot.Variant v); +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/pig/PigAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/pig/PigAccessor.java new file mode 100644 index 00000000000..e0ad6acb08d --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/pig/PigAccessor.java @@ -0,0 +1,38 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.world.entity.animal.pig; + +import net.minecraft.core.Holder; +import net.minecraft.world.entity.animal.pig.Pig; +import net.minecraft.world.entity.animal.pig.PigVariant; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(Pig.class) +public interface PigAccessor { + + @Invoker("setVariant") void invoker$setVariant(Holder $$0x); + +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/rabbit/RabbitAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/rabbit/RabbitAccessor.java new file mode 100644 index 00000000000..8bc5ce21117 --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/rabbit/RabbitAccessor.java @@ -0,0 +1,35 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.world.entity.animal.rabbit; + +import net.minecraft.world.entity.animal.rabbit.Rabbit; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(Rabbit.class) +public interface RabbitAccessor { + + @Invoker("setVariant") void invoker$setVariant(Rabbit.Variant v); +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/SheepAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/sheep/SheepAccessor.java similarity index 82% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/SheepAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/sheep/SheepAccessor.java index f876ba9205c..441882a66b9 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/SheepAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/sheep/SheepAccessor.java @@ -22,18 +22,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal; +package org.spongepowered.common.accessor.world.entity.animal.sheep; import net.minecraft.network.syncher.EntityDataAccessor; -import net.minecraft.world.entity.animal.Sheep; -import net.minecraft.world.item.DyeColor; -import net.minecraft.world.level.ItemLike; +import net.minecraft.world.entity.animal.sheep.Sheep; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.common.UntransformedAccessorError; -import java.util.Map; - @Mixin(Sheep.class) public interface SheepAccessor { @@ -41,8 +37,4 @@ public interface SheepAccessor { throw new UntransformedAccessorError(); } - @Accessor("ITEM_BY_DYE") static Map accessor$ITEM_BY_DYE() { - throw new UntransformedAccessorError(); - } - } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/TurtleAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/turtle/TurtleAccessor.java similarity index 72% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/TurtleAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/turtle/TurtleAccessor.java index e812ef22e99..84408ef5102 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/TurtleAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/turtle/TurtleAccessor.java @@ -22,32 +22,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal; +package org.spongepowered.common.accessor.world.entity.animal.turtle; import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.animal.Turtle; +import net.minecraft.world.entity.animal.turtle.Turtle; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; @Mixin(Turtle.class) public interface TurtleAccessor { - @Invoker("getHomePos") BlockPos invoker$getHomePos(); + @Accessor("homePos") BlockPos invoker$getHomePos(); - @Invoker("getTravelPos") BlockPos invoker$getTravelPos(); + @Accessor("travelPos") BlockPos invoker$getTravelPos(); - @Invoker("setTravelPos") void invoker$setTravelPos(final BlockPos travelPos); + @Accessor("travelPos") void invoker$setTravelPos(final BlockPos travelPos); @Invoker("setHasEgg") void invoker$setHasEgg(final boolean hasEgg); @Invoker("setLayingEgg") void invoker$setLayingEgg(final boolean layingEgg); - @Invoker("isGoingHome") boolean invoker$isGoingHome(); + @Accessor("goingHome") boolean invoker$isGoingHome(); - @Invoker("setGoingHome") void invoker$setGoingHome(final boolean goingHome); - - @Invoker("isTravelling") boolean invoker$isTravelling(); - - @Invoker("setTravelling") void invoker$setTravelling(final boolean travelling); + @Accessor("goingHome") void invoker$setGoingHome(final boolean goingHome); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/WolfAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/wolf/WolfAccessor.java similarity index 94% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/WolfAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/wolf/WolfAccessor.java index 3c1ba1ae088..b1f854f78c0 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/WolfAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/wolf/WolfAccessor.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal; +package org.spongepowered.common.accessor.world.entity.animal.wolf; import net.minecraft.network.syncher.EntityDataAccessor; -import net.minecraft.world.entity.animal.Wolf; +import net.minecraft.world.entity.animal.wolf.Wolf; import net.minecraft.world.item.DyeColor; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/decoration/ArmorStandAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/decoration/ArmorStandAccessor.java index f7793b8ce90..f3909b42155 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/decoration/ArmorStandAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/decoration/ArmorStandAccessor.java @@ -24,7 +24,6 @@ */ package org.spongepowered.common.accessor.world.entity.decoration; -import net.minecraft.core.Rotations; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.decoration.ArmorStand; import org.spongepowered.asm.mixin.Mixin; @@ -38,22 +37,10 @@ public interface ArmorStandAccessor { @Accessor("disabledSlots") void accessor$disabledSlots(final int disabledSlots); - @Accessor("leftArmPose") Rotations accessor$leftArmPose(); - - @Accessor("rightArmPose") Rotations accessor$rightArmPose(); - - @Accessor("leftLegPose") Rotations accessor$leftLegPose(); - - @Accessor("rightLegPose") Rotations accessor$rightLegPose(); - @Invoker("isDisabled") boolean invoker$isDisabled(final EquipmentSlot slot); @Invoker("setSmall") void invoker$setSmall(final boolean small); - @Invoker("setShowArms") void invoker$setShowArms(final boolean showArms); - - @Invoker("setNoBasePlate") void invoker$setNoBasePlate(final boolean noBasePlate); - @Invoker("setMarker") void invoker$setMarker(final boolean marker); @Invoker("hasPhysics") boolean invoker$hasPhysics(); diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/decoration/ItemFrameAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/decoration/ItemFrameAccessor.java index 7e866a74df3..8b863b2f790 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/decoration/ItemFrameAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/decoration/ItemFrameAccessor.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.accessor.world.entity.decoration; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.decoration.ItemFrame; import org.spongepowered.asm.mixin.Mixin; @@ -34,7 +35,7 @@ @Mixin(ItemFrame.class) public interface ItemFrameAccessor { - @Invoker("dropItem") void invoker$dropItem(final Entity entity, final boolean drop); + @Invoker("dropItem") void invoker$dropItem(final ServerLevel level, final Entity entity, final boolean drop); @Accessor("fixed") boolean accessor$fixed(); diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/decoration/painting/PaintingAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/decoration/painting/PaintingAccessor.java new file mode 100644 index 00000000000..eb4b8a1f96d --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/decoration/painting/PaintingAccessor.java @@ -0,0 +1,38 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.world.entity.decoration.painting; + +import net.minecraft.core.Holder; +import net.minecraft.world.entity.decoration.painting.Painting; +import net.minecraft.world.entity.decoration.painting.PaintingVariant; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(Painting.class) +public interface PaintingAccessor { + + @Invoker("setVariant") void invoker$setVariant(Holder newArt); + +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/item/PrimedTntAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/item/PrimedTntAccessor.java index 83b6c34bb07..ffc5a212e4f 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/item/PrimedTntAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/item/PrimedTntAccessor.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.accessor.world.entity.item; +import net.minecraft.world.entity.EntityReference; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.item.PrimedTnt; import org.spongepowered.asm.mixin.Mixin; @@ -32,6 +33,6 @@ @Mixin(PrimedTnt.class) public interface PrimedTntAccessor { - @Accessor("owner") void accessor$owner(final LivingEntity owner); + @Accessor("owner") void accessor$owner(final EntityReference owner); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/EvokerAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/EvokerAccessor.java similarity index 89% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/EvokerAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/EvokerAccessor.java index c45b9993bda..29293ef376c 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/EvokerAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/EvokerAccessor.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.monster; +package org.spongepowered.common.accessor.world.entity.monster.illager; -import net.minecraft.world.entity.animal.Sheep; -import net.minecraft.world.entity.monster.Evoker; +import net.minecraft.world.entity.animal.sheep.Sheep; +import net.minecraft.world.entity.monster.illager.Evoker; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/PillagerAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/PillagerAccessor.java similarity index 92% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/PillagerAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/PillagerAccessor.java index 7096fe2af7c..328797e1e1c 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/PillagerAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/PillagerAccessor.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.monster; +package org.spongepowered.common.accessor.world.entity.monster.illager; import net.minecraft.network.syncher.EntityDataAccessor; -import net.minecraft.world.entity.monster.Pillager; +import net.minecraft.world.entity.monster.illager.Pillager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.common.UntransformedAccessorError; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/SpellcasterIllagerAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/SpellcasterIllagerAccessor.java similarity index 92% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/SpellcasterIllagerAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/SpellcasterIllagerAccessor.java index 9ef5b5fa2f0..456016fb8b8 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/SpellcasterIllagerAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/SpellcasterIllagerAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.monster; +package org.spongepowered.common.accessor.world.entity.monster.illager; -import net.minecraft.world.entity.monster.SpellcasterIllager; +import net.minecraft.world.entity.monster.illager.SpellcasterIllager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/VindicatorAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/VindicatorAccessor.java similarity index 91% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/VindicatorAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/VindicatorAccessor.java index 4cfb51723b1..afedae4c32e 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/VindicatorAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/illager/VindicatorAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.monster; +package org.spongepowered.common.accessor.world.entity.monster.illager; -import net.minecraft.world.entity.monster.Vindicator; +import net.minecraft.world.entity.monster.illager.Vindicator; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/ZombifiedPiglinAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/zombie/ZombifiedPiglinAccessor.java similarity index 80% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/ZombifiedPiglinAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/zombie/ZombifiedPiglinAccessor.java index 4ab6bb3e630..321b56e95aa 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/ZombifiedPiglinAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/monster/zombie/ZombifiedPiglinAccessor.java @@ -22,16 +22,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.monster; +package org.spongepowered.common.accessor.world.entity.monster.zombie; -import net.minecraft.world.entity.monster.ZombifiedPiglin; +import net.minecraft.world.entity.monster.zombie.ZombifiedPiglin; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(ZombifiedPiglin.class) public interface ZombifiedPiglinAccessor { - @Accessor("remainingPersistentAngerTime") int accessor$remainingPersistentAngerTime(); + @Accessor("persistentAngerEndTime") long accessor$remainingPersistentAngerTime(); - @Accessor("remainingPersistentAngerTime") void accessor$remainingPersistentAngerTime(final int remainingPersistentAngerTime); + @Accessor("persistentAngerEndTime") void accessor$remainingPersistentAngerTime(final long remainingPersistentAngerTime); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/npc/AbstractVillagerAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/npc/villager/AbstractVillagerAccessor.java similarity index 92% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/npc/AbstractVillagerAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/npc/villager/AbstractVillagerAccessor.java index 3bcb04f866b..4f78ae7cfd8 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/npc/AbstractVillagerAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/npc/villager/AbstractVillagerAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.npc; +package org.spongepowered.common.accessor.world.entity.npc.villager; -import net.minecraft.world.entity.npc.AbstractVillager; +import net.minecraft.world.entity.npc.villager.AbstractVillager; import net.minecraft.world.item.trading.MerchantOffers; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/npc/wanderingtrader/WanderingTraderSpawnerAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/npc/wanderingtrader/WanderingTraderSpawnerAccessor.java new file mode 100644 index 00000000000..e704ff22e78 --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/npc/wanderingtrader/WanderingTraderSpawnerAccessor.java @@ -0,0 +1,37 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.world.entity.npc.wanderingtrader; + +import net.minecraft.world.entity.npc.wanderingtrader.WanderingTraderSpawner; +import net.minecraft.world.level.saveddata.WanderingTraderData; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(WanderingTraderSpawner.class) +public interface WanderingTraderSpawnerAccessor { + + @Invoker("getTraderData") WanderingTraderData invoker$getTraderData(); + +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/player/PlayerAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/player/PlayerAccessor.java index 9a12ace2ef0..b419f06bf7b 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/player/PlayerAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/player/PlayerAccessor.java @@ -24,13 +24,16 @@ */ package org.spongepowered.common.accessor.world.entity.player; -import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.server.permissions.PermissionSet; import net.minecraft.world.entity.player.Player; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.common.UntransformedAccessorError; +import java.util.OptionalInt; + @Mixin(Player.class) public interface PlayerAccessor { @@ -42,21 +45,14 @@ public interface PlayerAccessor { throw new UntransformedAccessorError(); } - @Accessor("DATA_PLAYER_MODE_CUSTOMISATION") static EntityDataAccessor accessor$DATA_PLAYER_MODE_CUSTOMISATION() { - throw new UntransformedAccessorError(); - } - - @Accessor("DATA_PLAYER_MAIN_HAND") static EntityDataAccessor accessor$DATA_PLAYER_MAIN_HAND() { - throw new UntransformedAccessorError(); - } - - @Accessor("DATA_SHOULDER_LEFT") static EntityDataAccessor accessor$DATA_SHOULDER_LEFT() { + @Accessor("DATA_SHOULDER_PARROT_LEFT") static EntityDataAccessor accessor$DATA_SHOULDER_LEFT() { throw new UntransformedAccessorError(); } - @Accessor("DATA_SHOULDER_RIGHT") static EntityDataAccessor accessor$DATA_SHOULDER_RIGHT() { + @Accessor("DATA_SHOULDER_PARROT_RIGHT") static EntityDataAccessor accessor$DATA_SHOULDER_RIGHT() { throw new UntransformedAccessorError(); } + @Invoker("permissions") PermissionSet invoker$permissions(); @Accessor("sleepCounter") void accessor$sleepCounter(final int sleepCounter); diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/ShulkerBulletAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/ShulkerBulletAccessor.java index 0ef473a6dca..d607c02d1f4 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/ShulkerBulletAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/ShulkerBulletAccessor.java @@ -26,6 +26,7 @@ import net.minecraft.core.Direction; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityReference; import net.minecraft.world.entity.projectile.ShulkerBullet; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; @@ -34,9 +35,9 @@ @Mixin(ShulkerBullet.class) public interface ShulkerBulletAccessor { - @Accessor("finalTarget") Entity accessor$finalTarget(); + @Accessor("finalTarget") EntityReference accessor$finalTarget(); - @Accessor("finalTarget") void accessor$finalTarget(final Entity finalTarget); + @Accessor("finalTarget") void accessor$finalTarget(final EntityReference finalTarget); @Accessor("currentMoveDirection") @Nullable Direction accessor$currentMoveDirection(); diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/ProjectileAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/arrow/AbstractArrowAccessor.java similarity index 87% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/ProjectileAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/arrow/AbstractArrowAccessor.java index 92ee000e10e..b1933f77215 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/ProjectileAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/arrow/AbstractArrowAccessor.java @@ -22,16 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.projectile; +package org.spongepowered.common.accessor.world.entity.projectile.arrow; -import net.minecraft.world.entity.projectile.Projectile; +import net.minecraft.world.entity.projectile.arrow.AbstractArrow; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -import java.util.UUID; +@Mixin(AbstractArrow.class) +public interface AbstractArrowAccessor { -@Mixin(Projectile.class) -public interface ProjectileAccessor { + @Accessor("baseDamage") double accessor$baseDamage(); - @Accessor("ownerUUID") UUID accessor$ownerUUID(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/ArrowAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/arrow/ArrowAccessor.java similarity index 96% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/ArrowAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/arrow/ArrowAccessor.java index 7e197ba4819..a8b2151b4d6 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/ArrowAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/projectile/arrow/ArrowAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.projectile; +package org.spongepowered.common.accessor.world.entity.projectile.arrow; -import net.minecraft.world.entity.projectile.Arrow; +import net.minecraft.world.entity.projectile.arrow.Arrow; import net.minecraft.world.item.alchemy.PotionContents; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/raid/RaidsAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/raid/RaidsAccessor.java index 149e320e1eb..a606054143a 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/raid/RaidsAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/raid/RaidsAccessor.java @@ -24,16 +24,17 @@ */ package org.spongepowered.common.accessor.world.entity.raid; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.world.entity.raid.Raid; import net.minecraft.world.entity.raid.Raids; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -import java.util.Map; @Mixin(Raids.class) public interface RaidsAccessor { - @Accessor("raidMap") Map accessor$raidMap(); + @Accessor("raidMap") + Int2ObjectMap accessor$raidMap(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/MushroomCow_MushroomTypeAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/boat/AbstractBoatAccessor.java similarity index 83% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/MushroomCow_MushroomTypeAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/boat/AbstractBoatAccessor.java index 0bcfb85fffd..21008dca1ce 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/animal/MushroomCow_MushroomTypeAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/boat/AbstractBoatAccessor.java @@ -22,15 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.animal; +package org.spongepowered.common.accessor.world.entity.vehicle.boat; -import net.minecraft.world.entity.animal.MushroomCow; +import net.minecraft.world.entity.vehicle.boat.AbstractBoat; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(MushroomCow.MushroomType.class) -public interface MushroomCow_MushroomTypeAccessor { +@Mixin(AbstractBoat.class) +public interface AbstractBoatAccessor { - @Accessor("type") String accessor$type(); + @Accessor("status") AbstractBoat.Status accessor$status(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/MinecartFurnaceAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/minecart/MinecartFurnaceAccessor.java similarity index 91% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/MinecartFurnaceAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/minecart/MinecartFurnaceAccessor.java index 12dbc06a13e..b60733608e3 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/MinecartFurnaceAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/minecart/MinecartFurnaceAccessor.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.vehicle; +package org.spongepowered.common.accessor.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.MinecartFurnace; +import net.minecraft.world.entity.vehicle.minecart.MinecartFurnace; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/food/FoodDataAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/food/FoodDataAccessor.java index d01f6e281c3..4d7892de036 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/food/FoodDataAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/food/FoodDataAccessor.java @@ -37,4 +37,6 @@ public interface FoodDataAccessor { @Accessor("exhaustionLevel") void accessor$exhaustionLevel(final float exhaustionLevel); + @Accessor("exhaustionLevel") float accessor$exhaustionLevel(); + } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/inventory/AbstractContainerMenuAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/inventory/AbstractContainerMenuAccessor.java index b08dd86d7c6..1ce430b88f2 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/inventory/AbstractContainerMenuAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/inventory/AbstractContainerMenuAccessor.java @@ -31,6 +31,7 @@ import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; import java.util.List; @@ -43,4 +44,6 @@ public interface AbstractContainerMenuAccessor { @Accessor("containerListeners") List accessor$containerListeners(); + @Invoker("synchronizeCarriedToRemote") void accessor$synchronizeCarriedToRemote(); + } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/inventory/CraftingMenuAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/inventory/AbstractCraftingMenuAccessor.java similarity index 91% rename from src/accessors/java/org/spongepowered/common/accessor/world/inventory/CraftingMenuAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/inventory/AbstractCraftingMenuAccessor.java index dce50afccdf..354247b48d5 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/inventory/CraftingMenuAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/inventory/AbstractCraftingMenuAccessor.java @@ -24,13 +24,13 @@ */ package org.spongepowered.common.accessor.world.inventory; +import net.minecraft.world.inventory.AbstractCraftingMenu; import net.minecraft.world.inventory.CraftingContainer; -import net.minecraft.world.inventory.CraftingMenu; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(CraftingMenu.class) -public interface CraftingMenuAccessor { +@Mixin(AbstractCraftingMenu.class) +public interface AbstractCraftingMenuAccessor { @Accessor("craftSlots") CraftingContainer accessor$craftSlots(); diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/inventory/HorseInventoryMenuAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/inventory/AbstractMountInventoryMenuAccessor.java similarity index 84% rename from src/accessors/java/org/spongepowered/common/accessor/world/inventory/HorseInventoryMenuAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/inventory/AbstractMountInventoryMenuAccessor.java index 045f703fb0d..f676a038854 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/inventory/HorseInventoryMenuAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/inventory/AbstractMountInventoryMenuAccessor.java @@ -24,14 +24,14 @@ */ package org.spongepowered.common.accessor.world.inventory; -import net.minecraft.world.entity.animal.horse.AbstractHorse; -import net.minecraft.world.inventory.HorseInventoryMenu; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.inventory.AbstractMountInventoryMenu; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(HorseInventoryMenu.class) -public interface HorseInventoryMenuAccessor { +@Mixin(AbstractMountInventoryMenu.class) +public interface AbstractMountInventoryMenuAccessor { - @Accessor("horse") AbstractHorse accessor$horse(); + @Accessor("mount") LivingEntity accessor$mount(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/item/AdventureModePredicateAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/item/AdventureModePredicateAccessor.java index 4c0425100ce..e325ffc4287 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/item/AdventureModePredicateAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/item/AdventureModePredicateAccessor.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.accessor.world.item; -import net.minecraft.advancements.critereon.BlockPredicate; +import net.minecraft.advancements.predicates.BlockPredicate; import net.minecraft.world.item.AdventureModePredicate; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/item/component/CustomDataAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/item/component/CustomDataAccessor.java index 56e1b1af747..cdac3d9bcb2 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/item/component/CustomDataAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/item/component/CustomDataAccessor.java @@ -27,12 +27,15 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.component.CustomData; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.common.UntransformedInvokerError; @Mixin(CustomData.class) public interface CustomDataAccessor { + @Accessor("tag") CompoundTag accessor$tag(); + @Invoker("") static CustomData invoker$new(CompoundTag tag) { throw new UntransformedInvokerError(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/item/context/UseOnContextAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/item/context/UseOnContextAccessor.java new file mode 100644 index 00000000000..9c8044785b6 --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/world/item/context/UseOnContextAccessor.java @@ -0,0 +1,36 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.world.item.context; + +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.phys.BlockHitResult; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(UseOnContext.class) +public interface UseOnContextAccessor { + + @Accessor("hitResult") BlockHitResult accessor$hitResult(); +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/item/crafting/RecipeMapAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/item/crafting/RecipeMapAccessor.java new file mode 100644 index 00000000000..f7ed452f1de --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/world/item/crafting/RecipeMapAccessor.java @@ -0,0 +1,41 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.world.item.crafting; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.RecipeMap; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(RecipeMap.class) +public interface RecipeMapAccessor { + + @Accessor("byKey") + Map>, RecipeHolder> accessor$byKey(); +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundInteractPacket_InteractionActionAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/item/crafting/ShapelessRecipeAccessor.java similarity index 79% rename from src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundInteractPacket_InteractionActionAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/item/crafting/ShapelessRecipeAccessor.java index 33e1391a1d8..b0343958027 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundInteractPacket_InteractionActionAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/item/crafting/ShapelessRecipeAccessor.java @@ -22,15 +22,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.network.protocol.game; +package org.spongepowered.common.accessor.world.item.crafting; -import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.ShapelessRecipe; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(targets = "net.minecraft.network.protocol.game.ServerboundInteractPacket$InteractionAction") -public interface ServerboundInteractPacket_InteractionActionAccessor { - - @Accessor("hand") InteractionHand accessor$hand(); +import java.util.List; +@Mixin(ShapelessRecipe.class) +public interface ShapelessRecipeAccessor { + @Accessor("ingredients") List accessor$ingredients(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/item/enchantment/ItemEnchantmentsAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/item/enchantment/ItemEnchantmentsAccessor.java index d3a86c01221..66b55ba858a 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/item/enchantment/ItemEnchantmentsAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/item/enchantment/ItemEnchantmentsAccessor.java @@ -36,11 +36,10 @@ @Mixin(ItemEnchantments.class) public interface ItemEnchantmentsAccessor { - @Accessor("showInTooltip") boolean accessor$showInTooltip(); @Accessor("enchantments") Object2IntOpenHashMap> accessor$enchantments(); - @Invoker("") static ItemEnchantments invoker$new(Object2IntOpenHashMap> $$0, boolean $$1) { + @Invoker("") static ItemEnchantments invoker$new(Object2IntOpenHashMap> $$0) { throw new UntransformedInvokerError(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/BaseCommandBlockAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/BaseCommandBlockAccessor.java index d9db6bd2d9e..9b527e7d30c 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/BaseCommandBlockAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/BaseCommandBlockAccessor.java @@ -25,9 +25,11 @@ package org.spongepowered.common.accessor.world.level; import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.BaseCommandBlock; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; @Mixin(BaseCommandBlock.class) public interface BaseCommandBlockAccessor { @@ -40,4 +42,6 @@ public interface BaseCommandBlockAccessor { @Accessor("command") void accessor$command(final String command); + @Invoker("createSource") BaseCommandBlock.CloseableCommandBlockSource invoker$createSource(ServerLevel level); + } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/BaseSpawnerAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/BaseSpawnerAccessor.java index 594ce45ca9d..2ea7c95ad05 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/BaseSpawnerAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/BaseSpawnerAccessor.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.accessor.world.level; -import net.minecraft.util.random.SimpleWeightedRandomList; +import net.minecraft.util.random.WeightedList; import net.minecraft.world.level.BaseSpawner; import net.minecraft.world.level.SpawnData; import org.spongepowered.asm.mixin.Mixin; @@ -37,9 +37,9 @@ public interface BaseSpawnerAccessor { @Accessor("spawnDelay") void accessor$spawnDelay(final int spawnDelay); - @Accessor("spawnPotentials") SimpleWeightedRandomList accessor$spawnPotentials(); + @Accessor("spawnPotentials") WeightedList accessor$spawnPotentials(); - @Accessor("spawnPotentials") void accessor$spawnPotentials(SimpleWeightedRandomList newData); + @Accessor("spawnPotentials") void accessor$spawnPotentials(WeightedList newData); @Accessor("nextSpawnData") SpawnData accessor$nextSpawnData(); @Accessor("nextSpawnData") void accessor$nextSpawnData(SpawnData spawnData); diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/GameRulesAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/GameRulesAccessor.java index 0e2dbdfff3e..ff2d062861f 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/GameRulesAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/GameRulesAccessor.java @@ -24,20 +24,16 @@ */ package org.spongepowered.common.accessor.world.level; -import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.gamerules.GameRuleMap; +import net.minecraft.world.level.gamerules.GameRules; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -import org.spongepowered.common.UntransformedAccessorError; -import java.util.Map; @Mixin(GameRules.class) public interface GameRulesAccessor { - @Accessor("rules") Map, GameRules.Value> accessor$rules(); + @Accessor("rules") GameRuleMap accessor$rules(); - @Accessor("GAME_RULE_TYPES") static Map, GameRules.Type> accessor$GAME_RULE_TYPES() { - throw new UntransformedAccessorError(); - } } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/GameRules_ValueAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/GameRules_ValueAccessor.java index 9b0e219a7e6..476ad30520b 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/GameRules_ValueAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/GameRules_ValueAccessor.java @@ -24,13 +24,14 @@ */ package org.spongepowered.common.accessor.world.level; -import net.minecraft.world.level.GameRules; +import com.mojang.serialization.DataResult; +import net.minecraft.world.level.gamerules.GameRule; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; -@Mixin(GameRules.Value.class) +@Mixin(GameRule.class) public interface GameRules_ValueAccessor { - @Invoker("deserialize") void invoker$deserialize(final String serialized); + @Invoker("deserialize") DataResult invoker$deserialize(final String serialized); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/ExplosionAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/ServerExplosionAccessor.java similarity index 85% rename from src/accessors/java/org/spongepowered/common/accessor/world/level/ExplosionAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/level/ServerExplosionAccessor.java index 6f3e7f1f733..68550648525 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/ExplosionAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/ServerExplosionAccessor.java @@ -24,23 +24,18 @@ */ package org.spongepowered.common.accessor.world.level; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Explosion; -import net.minecraft.world.level.Level; +import net.minecraft.world.level.ServerExplosion; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(Explosion.class) -public interface ExplosionAccessor { +@Mixin(ServerExplosion.class) +public interface ServerExplosionAccessor { @Accessor("blockInteraction") Explosion.BlockInteraction accessor$blockInteraction(); - @Accessor("level") Level accessor$level(); - - @Accessor("x") double accessor$x(); - - @Accessor("y") double accessor$y(); - - @Accessor("z") double accessor$z(); + @Accessor("level") ServerLevel accessor$level(); @Accessor("radius") float accessor$radius(); diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/LevelSettingsAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/TicketStorageAccessor.java similarity index 80% rename from src/accessors/java/org/spongepowered/common/accessor/world/level/LevelSettingsAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/level/TicketStorageAccessor.java index 75e65bc5b83..41b774f440e 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/LevelSettingsAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/TicketStorageAccessor.java @@ -24,17 +24,16 @@ */ package org.spongepowered.common.accessor.world.level; -import net.minecraft.world.level.LevelSettings; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.server.level.Ticket; +import net.minecraft.world.level.TicketStorage; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(LevelSettings.class) -public interface LevelSettingsAccessor { +import java.util.List; - @Accessor("hardcore") - @Mutable void accessor$hardcore(final boolean hardcore); +@Mixin(TicketStorage.class) +public interface TicketStorageAccessor { - @Accessor("allowCommands") - @Mutable void accessor$allowCommands(final boolean allowCommands); + @Accessor("tickets") Long2ObjectOpenHashMap> accessor$tickets(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/biome/BiomeAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/biome/BiomeAccessor.java index 80b9378b46b..bfaea3db80f 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/biome/BiomeAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/biome/BiomeAccessor.java @@ -24,13 +24,17 @@ */ package org.spongepowered.common.accessor.world.level.biome; +import net.minecraft.core.BlockPos; import net.minecraft.world.level.biome.Biome; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; @Mixin(Biome.class) public interface BiomeAccessor { @Accessor("climateSettings") Biome.ClimateSettings accessor$climateSettings(); + @Invoker("getTemperature") float shadow$getTemperature(BlockPos pos, int seaLevel); + } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/AbstractFurnaceBlockEntityAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/AbstractFurnaceBlockEntityAccessor.java index 2ec3b5a53e5..784fb5e1a0c 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/AbstractFurnaceBlockEntityAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/AbstractFurnaceBlockEntityAccessor.java @@ -34,17 +34,17 @@ @Mixin(AbstractFurnaceBlockEntity.class) public interface AbstractFurnaceBlockEntityAccessor { - @Accessor("litTime") int accessor$litTime(); + @Accessor("litTotalTime") int accessor$litTime(); - @Accessor("litTime") void accessor$litTime(final int litTime); + @Accessor("litTotalTime") void accessor$litTime(final int litTime); - @Accessor("litDuration") int accessor$litDuration(); + @Accessor("litTimeRemaining") int accessor$litDuration(); - @Accessor("litDuration") void accessor$litDuration(final int litDuration); + @Accessor("litTimeRemaining") void accessor$litDuration(final int litDuration); - @Accessor("cookingProgress") int accessor$cookingProgress(); + @Accessor("cookingTimer") int accessor$cookingProgress(); - @Accessor("cookingProgress") void accessor$cookingProgress(final int cookingProgress); + @Accessor("cookingTimer") void accessor$cookingProgress(final int cookingProgress); @Accessor("cookingTotalTime") int accessor$cookingTotalTime(); diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/ConduitBlockEntityAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/ConduitBlockEntityAccessor.java index 0846872d3af..ef9914cd4a3 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/ConduitBlockEntityAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/ConduitBlockEntityAccessor.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.accessor.world.level.block.entity; +import net.minecraft.world.entity.EntityReference; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.block.entity.ConduitBlockEntity; import org.checkerframework.checker.nullness.qual.Nullable; @@ -33,8 +34,8 @@ @Mixin(ConduitBlockEntity.class) public interface ConduitBlockEntityAccessor { - @Accessor("destroyTarget") @Nullable LivingEntity accessor$destroyTarget(); + @Accessor("destroyTarget") @Nullable EntityReference accessor$destroyTarget(); - @Accessor("destroyTarget") void accessor$setDestroyTarget(@Nullable LivingEntity target); + @Accessor("destroyTarget") void accessor$setDestroyTarget(@Nullable EntityReference target); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/HopperBlockEntityAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/HopperBlockEntityAccessor.java index 2a6b9c55d25..95c1f423fae 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/HopperBlockEntityAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/HopperBlockEntityAccessor.java @@ -24,17 +24,9 @@ */ package org.spongepowered.common.accessor.world.level.block.entity; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.Container; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.HopperBlockEntity; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -import org.spongepowered.asm.mixin.gen.Invoker; -import org.spongepowered.common.UntransformedInvokerError; @Mixin(HopperBlockEntity.class) public interface HopperBlockEntityAccessor { @@ -42,25 +34,4 @@ public interface HopperBlockEntityAccessor { @Accessor("cooldownTime") int accessor$cooldownTime(); @Accessor("cooldownTime") void accessor$cooldownTime(final int cooldownTime); - - @Invoker("tryMoveInItem") - static ItemStack invoker$tryMoveInItem(final @Nullable Container source, final Container destination, final ItemStack stack, final int index, - final @Nullable Direction direction) { - throw new UntransformedInvokerError(); - } - - @Invoker("getSlots") - static int[] invoker$getSlots(final Container inventory, final Direction side) { - throw new UntransformedInvokerError(); - } - - @Invoker("getContainerAt") - static Container invoker$getContainerAt(Level var0, BlockPos blockPos) { - throw new UntransformedInvokerError(); - } - - @Invoker("isFullContainer") - static boolean invoker$isFullContainer(Container param0, Direction param1) { - throw new UntransformedInvokerError(); - } } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/SkullBlockEntityAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/SkullBlockEntityAccessor.java index e38e9af68a4..2113216a8a7 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/SkullBlockEntityAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/SkullBlockEntityAccessor.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.accessor.world.level.block.entity; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.block.entity.SkullBlockEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @@ -32,8 +32,7 @@ @Mixin(SkullBlockEntity.class) public interface SkullBlockEntityAccessor { - @Accessor("noteBlockSound") void accessor$noteBlockSound(final ResourceLocation location); - - @Accessor("noteBlockSound") ResourceLocation accessor$noteBlockSound(); + @Accessor("noteBlockSound") void accessor$noteBlockSound(final Identifier location); + @Accessor("noteBlockSound") Identifier accessor$noteBlockSound(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/trialspawner/TrialSpawnerAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/trialspawner/TrialSpawnerAccessor.java index 2b34f3284d2..e9c04f3bb7a 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/trialspawner/TrialSpawnerAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/trialspawner/TrialSpawnerAccessor.java @@ -25,7 +25,6 @@ package org.spongepowered.common.accessor.world.level.block.entity.trialspawner; import net.minecraft.world.level.block.entity.trialspawner.TrialSpawner; -import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerConfig; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; @@ -33,6 +32,5 @@ @Mixin(TrialSpawner.class) public interface TrialSpawnerAccessor { - @Mutable @Accessor("normalConfig") void accessor$normalConfig(TrialSpawnerConfig normalConfig); - @Mutable @Accessor("ominousConfig") void accessor$ominousConfig(TrialSpawnerConfig ominousConfig); + @Mutable @Accessor("config") void accessor$config(TrialSpawner.FullConfig normalConfig); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/trialspawner/TrialSpawnerDataAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/trialspawner/TrialSpawnerDataAccessor.java index f5de3ae6590..fa5e807d684 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/trialspawner/TrialSpawnerDataAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/entity/trialspawner/TrialSpawnerDataAccessor.java @@ -25,7 +25,7 @@ package org.spongepowered.common.accessor.world.level.block.entity.trialspawner; import net.minecraft.world.level.SpawnData; -import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerData; +import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerStateData; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @@ -33,7 +33,7 @@ import java.util.Set; import java.util.UUID; -@Mixin(TrialSpawnerData.class) +@Mixin(TrialSpawnerStateData.class) public interface TrialSpawnerDataAccessor { @Accessor("nextSpawnData") Optional accessor$nextSpawnData(); diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/state/StateHolderAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/state/StateHolderAccessor.java index 8e1e4e05408..7e1331995f3 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/block/state/StateHolderAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/block/state/StateHolderAccessor.java @@ -29,12 +29,12 @@ import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.common.UntransformedInvokerError; -import java.util.Collection; +import java.util.List; @Mixin(StateHolder.class) public interface StateHolderAccessor { - @Invoker("findNextInCollection") static T invoker$findNextInCollection(final Collection collection, final T element) { + @Invoker("findNextInCollection") static T invoker$findNextInCollection(final List $$0, final T $$1) { throw new UntransformedInvokerError(); } } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/chunk/LevelChunkAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/chunk/LevelChunkAccessor.java index f957f9ed1bb..e25d3bbc67a 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/chunk/LevelChunkAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/chunk/LevelChunkAccessor.java @@ -27,6 +27,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.TickingBlockEntity; import net.minecraft.world.level.chunk.LevelChunk; +import org.slf4j.Logger; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; @@ -42,4 +43,9 @@ public interface LevelChunkAccessor { throw new UntransformedAccessorError(); } + @Accessor("LOGGER") + static Logger accessor$LOGGER() { + throw new UntransformedAccessorError(); + } + } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/chunk/storage/ChunkStorageAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/chunk/storage/ChunkStorageAccessor.java deleted file mode 100644 index ce533c3c8c3..00000000000 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/chunk/storage/ChunkStorageAccessor.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.accessor.world.level.chunk.storage; - -import net.minecraft.world.level.chunk.storage.ChunkStorage; -import net.minecraft.world.level.chunk.storage.IOWorker; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(ChunkStorage.class) -public interface ChunkStorageAccessor { - - @Accessor("worker") IOWorker accessor$worker(); -} diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/dimension/DimensionTypeAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/dimension/DimensionTypeAccessor.java index d0f7d6f4fd3..e90a8dac1e0 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/dimension/DimensionTypeAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/dimension/DimensionTypeAccessor.java @@ -28,12 +28,9 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -import java.util.OptionalLong; @Mixin(DimensionType.class) public interface DimensionTypeAccessor { - @Accessor("fixedTime") OptionalLong accessor$fixedTime(); - @Accessor("ambientLight") float accessor$ambientLight(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/dimension/end/EndDragonFightAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/dimension/end/EnderDragonFightAccessor.java similarity index 91% rename from src/accessors/java/org/spongepowered/common/accessor/world/level/dimension/end/EndDragonFightAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/level/dimension/end/EnderDragonFightAccessor.java index 7999d25ef76..3c0c90170cc 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/dimension/end/EndDragonFightAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/dimension/end/EnderDragonFightAccessor.java @@ -25,12 +25,12 @@ package org.spongepowered.common.accessor.world.level.dimension.end; import net.minecraft.server.level.ServerBossEvent; -import net.minecraft.world.level.dimension.end.EndDragonFight; +import net.minecraft.world.level.dimension.end.EnderDragonFight; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(EndDragonFight.class) -public interface EndDragonFightAccessor { +@Mixin(EnderDragonFight.class) +public interface EnderDragonFightAccessor { @Accessor("dragonEvent") ServerBossEvent accessor$dragonEvent(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/portal/DimensionTransitionAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/portal/TeleportTransitionAccessor.java similarity index 91% rename from src/accessors/java/org/spongepowered/common/accessor/world/level/portal/DimensionTransitionAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/level/portal/TeleportTransitionAccessor.java index 8c7801fb3f4..af1f02384ae 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/level/portal/DimensionTransitionAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/portal/TeleportTransitionAccessor.java @@ -25,13 +25,13 @@ package org.spongepowered.common.accessor.world.level.portal; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.portal.DimensionTransition; +import net.minecraft.world.level.portal.TeleportTransition; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(DimensionTransition.class) -public interface DimensionTransitionAccessor { +@Mixin(TeleportTransition.class) +public interface TeleportTransitionAccessor { @Accessor("newLevel") @Mutable void accessor$newLevel(ServerLevel level); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/BoatAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/saveddata/maps/MapIndexAccessor.java similarity index 84% rename from src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/BoatAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/level/saveddata/maps/MapIndexAccessor.java index 13c13fa8e5f..fecca3eaddd 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/world/entity/vehicle/BoatAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/saveddata/maps/MapIndexAccessor.java @@ -22,15 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.world.entity.vehicle; +package org.spongepowered.common.accessor.world.level.saveddata.maps; -import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.level.saveddata.maps.MapIndex; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(Boat.class) -public interface BoatAccessor { +@Mixin(MapIndex.class) +public interface MapIndexAccessor { - @Accessor("status") Boat.Status accessor$status(); + @Accessor("lastMapId") int accessor$getLastIndexID(); } diff --git a/src/accessors/java/org/spongepowered/common/accessor/world/level/storage/PrimaryLevelDataAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/storage/PrimaryLevelDataAccessor.java new file mode 100644 index 00000000000..aa58bcfdee3 --- /dev/null +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/storage/PrimaryLevelDataAccessor.java @@ -0,0 +1,36 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.accessor.world.level.storage; + +import net.minecraft.world.level.storage.PrimaryLevelData; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(PrimaryLevelData.class) +public interface PrimaryLevelDataAccessor { + + @Accessor("specialWorldProperty") @Mutable void accessor$specialWorldProperty(PrimaryLevelData.SpecialWorldProperty specialWorldProperty); +} diff --git a/src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundInteractPacketAccessor.java b/src/accessors/java/org/spongepowered/common/accessor/world/level/storage/TagValueInputAccessor.java similarity index 80% rename from src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundInteractPacketAccessor.java rename to src/accessors/java/org/spongepowered/common/accessor/world/level/storage/TagValueInputAccessor.java index 5e9220c9a0e..012212391fe 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/network/protocol/game/ServerboundInteractPacketAccessor.java +++ b/src/accessors/java/org/spongepowered/common/accessor/world/level/storage/TagValueInputAccessor.java @@ -22,15 +22,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.network.protocol.game; +package org.spongepowered.common.accessor.world.level.storage; -import net.minecraft.network.protocol.game.ServerboundInteractPacket; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.storage.TagValueInput; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(ServerboundInteractPacket.class) -public interface ServerboundInteractPacketAccessor { - - @Accessor("action") ServerboundInteractPacket.Action accessor$action(); +@Mixin(TagValueInput.class) +public interface TagValueInputAccessor { + @Accessor("input") @Final CompoundTag accessor$input(); } diff --git a/src/accessors/resources/mixins.sponge.accessors.json b/src/accessors/resources/mixins.sponge.accessors.json index 2e83d266c50..8eac34305cc 100644 --- a/src/accessors/resources/mixins.sponge.accessors.json +++ b/src/accessors/resources/mixins.sponge.accessors.json @@ -8,10 +8,7 @@ "world.level.entity.TransientEntitySectionManagerAccessor" ], "mixins": [ - "ChatFormattingAccessor", "advancements.CriteriaTriggersAccessor", - "advancements.critereon.MinMaxBounds_DoublesAccessor", - "advancements.critereon.MinMaxBounds_IntsAccessor", "commands.CommandSourceStackAccessor", "commands.arguments.DimensionArgumentAccessor", "commands.arguments.OperationArgumentAccessor", @@ -19,22 +16,22 @@ "commands.arguments.selector.options.EntitySelectorOptions_OptionAccessor", "commands.arguments.selector.options.EntitySelectorOptionsAccessor", "core.MappedRegistryAccessor", - "entity.passive.AbstractChestedHorseEntityAccessor", - "nbt.CompoundTagAccessor", + "entity.animal.equine.AbstractChestedHorseEntityAccessor", + "entity.animal.frog.FrogAccessor", + "entity.vehicle.minecart.AbstractMinecartAccessor", "network.ConnectionAccessor", - "network.chat.HoverEvent_ItemStackInfoAccessor", "network.chat.StyleAccessor", "network.protocol.game.ClientboundAddEntityPacketAccessor", "network.protocol.game.ClientboundPlayerInfoUpdatePacketAccessor", - "network.protocol.game.ServerboundInteractPacket_InteractionActionAccessor", - "network.protocol.game.ServerboundInteractPacket_InteractionAtLocationActionAccessor", - "network.protocol.game.ServerboundInteractPacketAccessor", "network.protocol.game.ServerboundMovePlayerPacketAccessor", "network.protocol.game.ServerboundMoveVehiclePacketAccessor", "network.protocol.handshake.ClientIntentionPacketAccessor", + "resources.RegistryDataLoader_ResourceManagerRegistryLoadTaskAccessor", + "resources.RegistryLoadTaskAccessor", "resources.ResourceKeyAccessor", + "server.MinecraftServer_ReloadableResourcesAccessor", "server.MinecraftServerAccessor", - "server.ServerFunctionManagerAccessor", + "server.bossevents.CustomBossEventsAccessor", "server.level.ChunkMap_TrackedEntityAccessor", "server.level.ChunkMapAccessor", "server.level.ServerChunkCacheAccessor", @@ -43,7 +40,6 @@ "server.level.ServerPlayerAccessor", "server.level.ServerPlayerGameModeAccessor", "server.level.TicketAccessor", - "server.level.TicketTypeAccessor", "server.network.ServerCommonPacketListenerImplAccessor", "server.network.ServerGamePacketListenerImplAccessor", "server.network.ServerLoginPacketListenerImplAccessor", @@ -56,17 +52,19 @@ "sounds.SoundEventAccessor", "stats.StatsCounterAccessor", "tags.TagEntryAccessor", + "util.BlockableEventLoopAccessor", "util.datafix.schemas.V100Accessor", "world.CompoundContainerAccessor", + "world.DamageSourcesAccessor", "world.damagesource.CombatTrackerAccessor", "world.entity.AgableMobAccessor", "world.entity.AreaEffectCloudAccessor", + "world.entity.AvatarAccessor", "world.entity.Display_BlockDisplayAccessor", "world.entity.Display_ItemDisplayAccessor", "world.entity.Display_TextDisplayAccessor", "world.entity.DisplayAccessor", "world.entity.EntityAccessor", - "world.entity.EntityTypeAccessor", "world.entity.ExperienceOrbAccessor", "world.entity.InteractionAccessor", "world.entity.LightningBoltAccessor", @@ -76,84 +74,91 @@ "world.entity.ai.attributes.AttributeMapAccessor", "world.entity.ai.targeting.TargetingConditionsAccessor", "world.entity.animal.AnimalAccessor", - "world.entity.animal.CatAccessor", - "world.entity.animal.FoxAccessor", - "world.entity.animal.MushroomCow_MushroomTypeAccessor", - "world.entity.animal.OcelotAccessor", - "world.entity.animal.PandaAccessor", - "world.entity.animal.PigAccessor", - "world.entity.animal.PufferfishAccessor", - "world.entity.animal.SheepAccessor", - "world.entity.animal.TropicalFishAccessor", - "world.entity.animal.TurtleAccessor", - "world.entity.animal.WolfAccessor", - "world.entity.animal.horse.HorseAccessor", - "world.entity.animal.horse.LlamaAccessor", - "world.entity.animal.horse.TraderLlamaAccessor", + "world.entity.animal.cow.MooshroomAccessor", + "world.entity.animal.equine.AbstractHorseAccessor", + "world.entity.animal.equine.HorseAccessor", + "world.entity.animal.equine.LlamaAccessor", + "world.entity.animal.equine.TraderLlamaAccessor", + "world.entity.animal.feline.CatAccessor", + "world.entity.animal.feline.OcelotAccessor", + "world.entity.animal.fish.PufferfishAccessor", + "world.entity.animal.fish.TropicalFishAccessor", + "world.entity.animal.fox.FoxAccessor", + "world.entity.animal.frog.TadpoleAccessor", + "world.entity.animal.panda.PandaAccessor", + "world.entity.animal.parrot.ParrotAccessor", + "world.entity.animal.pig.PigAccessor", + "world.entity.animal.rabbit.RabbitAccessor", + "world.entity.animal.sheep.SheepAccessor", + "world.entity.animal.turtle.TurtleAccessor", + "world.entity.animal.wolf.WolfAccessor", "world.entity.boss.enderdragon.phases.EnderDragonPhaseAccessor", "world.entity.boss.wither.WitherBossAccessor", "world.entity.decoration.ArmorStandAccessor", "world.entity.decoration.HangingEntityAccessor", "world.entity.decoration.ItemFrameAccessor", + "world.entity.decoration.painting.PaintingAccessor", "world.entity.item.FallingBlockEntityAccessor", "world.entity.item.PrimedTntAccessor", "world.entity.monster.BlazeAccessor", "world.entity.monster.CreeperAccessor", "world.entity.monster.EnderManAccessor", "world.entity.monster.EndermiteAccessor", - "world.entity.monster.EvokerAccessor", "world.entity.monster.GuardianAccessor", "world.entity.monster.PatrollingMonsterAccessor", "world.entity.monster.PhantomAccessor", - "world.entity.monster.PillagerAccessor", "world.entity.monster.RavagerAccessor", - "world.entity.monster.SlimeAccessor", - "world.entity.monster.SpellcasterIllagerAccessor", "world.entity.monster.VexAccessor", - "world.entity.monster.VindicatorAccessor", - "world.entity.monster.ZombifiedPiglinAccessor", - "world.entity.npc.AbstractVillagerAccessor", + "world.entity.monster.illager.EvokerAccessor", + "world.entity.monster.illager.PillagerAccessor", + "world.entity.monster.illager.SpellcasterIllagerAccessor", + "world.entity.monster.illager.VindicatorAccessor", + "world.entity.monster.zombie.ZombifiedPiglinAccessor", + "world.entity.npc.villager.AbstractVillagerAccessor", + "world.entity.npc.wanderingtrader.WanderingTraderSpawnerAccessor", "world.entity.player.AbilitiesAccessor", "world.entity.player.PlayerAccessor", - "world.entity.projectile.ArrowAccessor", "world.entity.projectile.EyeOfEnderAccessor", "world.entity.projectile.FireworkRocketEntityAccessor", "world.entity.projectile.FishingHookAccessor", - "world.entity.projectile.ProjectileAccessor", "world.entity.projectile.ShulkerBulletAccessor", + "world.entity.projectile.arrow.AbstractArrowAccessor", + "world.entity.projectile.arrow.ArrowAccessor", "world.entity.raid.RaidAccessor", "world.entity.raid.RaiderAccessor", "world.entity.raid.RaidsAccessor", - "world.entity.vehicle.BoatAccessor", - "world.entity.vehicle.MinecartFurnaceAccessor", + "world.entity.vehicle.boat.AbstractBoatAccessor", + "world.entity.vehicle.minecart.MinecartFurnaceAccessor", "world.food.FoodDataAccessor", "world.inventory.AbstractContainerMenuAccessor", + "world.inventory.AbstractCraftingMenuAccessor", "world.inventory.AbstractFurnaceMenuAccessor", + "world.inventory.AbstractMountInventoryMenuAccessor", "world.inventory.BeaconMenuAccessor", "world.inventory.BrewingStandMenuAccessor", - "world.inventory.CraftingMenuAccessor", "world.inventory.DispenserMenuAccessor", "world.inventory.HopperMenuAccessor", - "world.inventory.HorseInventoryMenuAccessor", "world.inventory.ItemCombinerMenuAccessor", "world.inventory.MerchantMenuAccessor", "world.inventory.ResultSlotAccessor", "world.inventory.SlotAccessor", "world.inventory.SmithingMenuAccessor", - "world.inventory.StonecutterMenuAccessor", "world.item.AdventureModePredicateAccessor", "world.item.ItemCooldowns_CooldownInstanceAccessor", "world.item.component.CustomDataAccessor", + "world.item.context.UseOnContextAccessor", + "world.item.crafting.RecipeMapAccessor", + "world.item.crafting.ShapelessRecipeAccessor", "world.item.enchantment.ItemEnchantmentsAccessor", "world.item.trading.MerchantOfferAccessor", "world.level.BaseCommandBlockAccessor", "world.level.BaseSpawnerAccessor", - "world.level.ExplosionAccessor", "world.level.GameRules_ValueAccessor", "world.level.GameRulesAccessor", "world.level.LevelAccessor", - "world.level.LevelSettingsAccessor", "world.level.NaturalSpawnerAccessor", + "world.level.ServerExplosionAccessor", + "world.level.TicketStorageAccessor", "world.level.biome.Biome_ClimateSettingsAccessor", "world.level.biome.BiomeAccessor", "world.level.biome.MobSpawnSettingsAccessor", @@ -195,20 +200,22 @@ "world.level.chunk.LevelChunk$BoundTickingBlockEntityAccessor", "world.level.chunk.LevelChunk$RebindableTickingBlockEntityWrapperAccessor", "world.level.chunk.LevelChunkAccessor", - "world.level.chunk.storage.ChunkStorageAccessor", "world.level.chunk.storage.IOWorker$PendingStoreAccessor", "world.level.chunk.storage.RegionFileAccessor", "world.level.chunk.storage.SimpleRegionStorageAccessor", "world.level.dimension.DimensionTypeAccessor", - "world.level.dimension.end.EndDragonFightAccessor", + "world.level.dimension.end.EnderDragonFightAccessor", "world.level.entity.EntityTickListAccessor", "world.level.entity.PersistentEntitySectionManagerAccessor", "world.level.levelgen.NoiseSettingsAccessor", "world.level.levelgen.flat.FlatLevelGeneratorSettingsAccessor", "world.level.levelgen.structure.pools.StructureTemplatePoolAccessor", - "world.level.portal.DimensionTransitionAccessor", + "world.level.portal.TeleportTransitionAccessor", + "world.level.saveddata.maps.MapIndexAccessor", "world.level.saveddata.maps.MapItemSavedDataAccessor", "world.level.storage.PlayerDataStorageAccessor", + "world.level.storage.PrimaryLevelDataAccessor", + "world.level.storage.TagValueInputAccessor", "world.phys.AABBAccessor", "world.scores.PlayerTeamAccessor", "world.scores.ScoreboardAccessor", diff --git a/src/applaunch/java/org/spongepowered/common/applaunch/plugin/PluginPlatformConstants.java b/src/applaunch/java-templates/org/spongepowered/common/applaunch/plugin/PluginPlatformConstants.java similarity index 75% rename from src/applaunch/java/org/spongepowered/common/applaunch/plugin/PluginPlatformConstants.java rename to src/applaunch/java-templates/org/spongepowered/common/applaunch/plugin/PluginPlatformConstants.java index f6ea7f509fd..fd6e9035fe7 100644 --- a/src/applaunch/java/org/spongepowered/common/applaunch/plugin/PluginPlatformConstants.java +++ b/src/applaunch/java-templates/org/spongepowered/common/applaunch/plugin/PluginPlatformConstants.java @@ -29,8 +29,12 @@ public final class PluginPlatformConstants { private PluginPlatformConstants() { } - public static final String METADATA_FILE_NAME = "sponge_plugins"; - public static final String METADATA_FILE_NAME_WITH_EXTENSION = PluginPlatformConstants.METADATA_FILE_NAME + ".json"; - public static final String METADATA_FILE_LOCATION = "META-INF/" + PluginPlatformConstants.METADATA_FILE_NAME_WITH_EXTENSION; + public static final String SPI_VERSION = "{{ pluginSpiVersion }}"; + + public static final String METADATA_FILE_PATH = "META-INF/sponge_plugins.json"; + public static final String TEST_METADATA_FILE_PATH = "META-INF/sponge_test_plugins.json"; + public static final String ENVIRONMENT_LOCATOR_VARIABLE_NAME = "SPONGE_PLUGINS"; + + public static final boolean IGNORE_FATAL_CONFLICTS = Boolean.getBoolean("sponge.discovery.ignoreFatalConflicts"); } diff --git a/src/applaunch/java/org/spongepowered/common/applaunch/AppLaunch.java b/src/applaunch/java/org/spongepowered/common/applaunch/AppLaunch.java index 0bd1e86e0c5..dad59f26612 100644 --- a/src/applaunch/java/org/spongepowered/common/applaunch/AppLaunch.java +++ b/src/applaunch/java/org/spongepowered/common/applaunch/AppLaunch.java @@ -28,6 +28,8 @@ import org.apache.logging.log4j.Logger; import org.spongepowered.common.applaunch.plugin.PluginPlatform; +import java.util.function.Supplier; + public final class AppLaunch { private static final Logger LOGGER = LogManager.getLogger("app launch"); @@ -37,12 +39,11 @@ public static Logger logger() { return AppLaunch.LOGGER; } - public static

P setPluginPlatform(final PluginPlatform pluginPlatform) { - if (AppLaunch.pluginPlatform != null) { - throw new RuntimeException("The plugin platform cannot be set twice! (Same classloader ?)"); + public static synchronized

P bootstrap(final Supplier

supplier) { + if (AppLaunch.pluginPlatform == null) { + AppLaunch.pluginPlatform = supplier.get(); } - AppLaunch.pluginPlatform = pluginPlatform; - return (P) pluginPlatform; + return (P) AppLaunch.pluginPlatform; } public static

P pluginPlatform() { diff --git a/src/applaunch/java/org/spongepowered/common/applaunch/metadata/PluginMetadataFixer.java b/src/applaunch/java/org/spongepowered/common/applaunch/metadata/PluginMetadataFixer.java deleted file mode 100644 index 620837e2f2d..00000000000 --- a/src/applaunch/java/org/spongepowered/common/applaunch/metadata/PluginMetadataFixer.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.applaunch.metadata; - -import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; -import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.MetadataContainer; -import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class PluginMetadataFixer { - private static final Set invalidPluginIds = new HashSet<>(); - - public static MetadataContainer fixPluginIds(final MetadataContainer container) { - boolean modified = false; - final List metadata = new ArrayList<>(); - - for (final PluginMetadata plugin : container.metadata()) { - final String id = plugin.id(); - if (id.indexOf('-') >= 0) { - final String newId = id.replace('-', '_'); - if (PluginMetadataFixer.invalidPluginIds.add(id)) { - AppLaunch.pluginPlatform().logger().warn("The dash character (-) is no longer supported in plugin ids.\n" + - "Plugin {} will be loaded as {}. If you are the developer of this plugin, please change the id.", id, newId); - } - - final StandardPluginMetadata.Builder pluginBuilder = StandardPluginMetadata.builder() - .from((StandardPluginMetadata) plugin).id(newId).entrypoint(plugin.entrypoint()); - plugin.name().ifPresent(pluginBuilder::name); - plugin.description().ifPresent(pluginBuilder::description); - - metadata.add(pluginBuilder.build()); - modified = true; - } else { - metadata.add((StandardPluginMetadata) plugin); - } - } - - if (!modified) { - return container; - } - - final MetadataContainer.Builder builder = container.toBuilder(); - builder.metadata(metadata); - - try { - return builder.build(); - } catch (InvalidVersionSpecificationException e) { - // This should not happen since we never modify the version. - throw new RuntimeException(e); - } - } -} diff --git a/src/applaunch/java/org/spongepowered/common/applaunch/plugin/PluginPlatform.java b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/PluginPlatform.java index 75a82e00b96..ab8e0d0b56f 100644 --- a/src/applaunch/java/org/spongepowered/common/applaunch/plugin/PluginPlatform.java +++ b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/PluginPlatform.java @@ -24,29 +24,113 @@ */ package org.spongepowered.common.applaunch.plugin; +import com.google.common.collect.ImmutableList; +import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.spongepowered.common.applaunch.config.LaunchConfig; import org.spongepowered.common.applaunch.config.TokenReplacement; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.common.applaunch.test.TestEnvironment; +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.blackboard.Blackboard; +import org.spongepowered.plugin.blackboard.Keys; +import org.spongepowered.plugin.builtin.StandardEnvironment; +import org.spongepowered.plugin.builtin.jvm.JVMKeys; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.List; -public interface PluginPlatform { +public abstract class PluginPlatform implements JVMPluginResource.Factory { + public static final Logger LOGGER = LogManager.getLogger("plugin"); - String version(); + private final Environment environment; + private final LaunchConfig config; + private final TokenReplacement tokens; + private final List pluginDirectories; - Logger logger(); + public PluginPlatform() { + this.environment = new StandardEnvironment(PluginPlatform.LOGGER); - boolean vanilla(); + this.logger().info("SpongePowered PLUGIN Subsystem Version={} Source={}", PluginPlatformConstants.SPI_VERSION, this.getCodeSource()); - Path baseDirectory(); + final Path baseDirectory = this.baseDirectory(); + final Path modsDirectory = this.modsDirectory(); - Path configDirectory(); + this.tokens = new TokenReplacement(); + this.tokens.register("BASE_DIR", baseDirectory); + this.tokens.register("CONFIG_DIR", this.configDirectory()); + this.tokens.register("MODS_DIR", modsDirectory); - LaunchConfig config(); + final Path additionalPluginsDirectory; + try { + this.config = LaunchConfig.load(baseDirectory, false); + additionalPluginsDirectory = Path.of(this.tokens.replace(this.config.additionalPluginsDirectory())); + Files.createDirectories(additionalPluginsDirectory); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + this.pluginDirectories = List.of(modsDirectory, additionalPluginsDirectory); - TokenReplacement tokens(); + ImmutableList.Builder metadataFilePaths = ImmutableList.builder(); + metadataFilePaths.add(PluginPlatformConstants.METADATA_FILE_PATH); + if (TestEnvironment.isActive()) { + metadataFilePaths.add(PluginPlatformConstants.TEST_METADATA_FILE_PATH); + } - List pluginDirectories(); + final Blackboard blackboard = this.environment.blackboard(); + blackboard.set(Keys.VERSION, PluginPlatformConstants.SPI_VERSION); + blackboard.set(Keys.BASE_DIRECTORY, baseDirectory); + blackboard.set(Keys.PLUGIN_DIRECTORIES, this.pluginDirectories); + blackboard.set(Keys.METADATA_FILE_PATHS, metadataFilePaths.build()); + blackboard.set(JVMKeys.ENVIRONMENT_LOCATOR_VARIABLE_NAME, PluginPlatformConstants.ENVIRONMENT_LOCATOR_VARIABLE_NAME); + blackboard.set(JVMKeys.JVM_PLUGIN_RESOURCE_FACTORY, this); + } + public final Logger logger() { + return PluginPlatform.LOGGER; + } + + protected String getCodeSource() { + try { + return this.getClass().getProtectionDomain().getCodeSource().getLocation().toString(); + } catch (final Throwable th) { + return "Unknown"; + } + } + + public abstract boolean vanilla(); + + public abstract Path baseDirectory(); + + public abstract Path configDirectory(); + + public abstract Path modsDirectory(); + + public abstract PluginDiscovery discovery(); + + /** + * Adds a callback that will be called once, when the platform loader is about to close. + * When the platform loader is closed, no new class can be loaded, so this is the last thing we can do. + */ + public abstract void addLoaderCloseCallback(AutoCloseable closeable); + + public final List pluginDirectories() { + return this.pluginDirectories; + } + + public final LaunchConfig config() { + return this.config; + } + + public final TokenReplacement tokens() { + return this.tokens; + } + + public final Environment environment() { + return this.environment; + } } diff --git a/src/applaunch/java/org/spongepowered/common/applaunch/plugin/PluginServiceLoader.java b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/PluginServiceLoader.java new file mode 100644 index 00000000000..71c333222c2 --- /dev/null +++ b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/PluginServiceLoader.java @@ -0,0 +1,67 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.applaunch.plugin; + +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.PluginService; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.function.Predicate; + +public abstract class PluginServiceLoader { + protected final Environment environment; + + protected PluginServiceLoader(final Environment environment) { + this.environment = environment; + } + + protected final List loadServices(final String serviceDescription, final Class serviceClass) { + return this.loadServices(serviceDescription, serviceClass, c -> true); + } + + protected final List loadServices(final String serviceDescription, final Class serviceClass, final Predicate> filter) { + final List services = new ArrayList<>(); + this.newServiceLoader(serviceClass).stream() + .filter(provider -> filter.test(provider.type())) + .forEach(provider -> { + final T service; + try { + service = provider.get(); + } catch (final ServiceConfigurationError e) { + this.environment.logger().error("Failed to initialize {}: {}", serviceDescription, provider.type().getName(), e); + return; + } + + this.environment.logger().info("Found {} '{}'.", serviceDescription, service.name()); + services.add(service); + }); + return services; + } + + protected abstract ServiceLoader newServiceLoader(final Class serviceClass); +} diff --git a/src/applaunch/java/org/spongepowered/common/applaunch/plugin/VersionChecker.java b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/VersionChecker.java new file mode 100644 index 00000000000..c01635f33e9 --- /dev/null +++ b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/VersionChecker.java @@ -0,0 +1,51 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.applaunch.plugin; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.VersionRange; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Objects; + +public class VersionChecker { + + public static boolean check(final @Nullable VersionRange requestedVersion, final ArtifactVersion dependencyVersion) { + if (requestedVersion == null || !requestedVersion.hasRestrictions()) { + // we don't care which version + return true; + } + // Maven Artifact version resolution has a bug(?) where VersionRange#containsVersion() + // returns false if there are no restrictions when logically it should be true because + // theoretically all versions are included. Except in our case, the recommended version + // might be populated, yet no restrictions are in the VersionRange object, because we + // want a specific version, which should be a restriction. + // + // Further, VersionRange#hasRestrictions() returns true even if VersionRange#getRestrictions() + // is empty as it accounts for if there is a recommended version. Thus, we have to do the check + // on the recommended version first... which might be null, hence the Objects.equals check. + return Objects.equals(requestedVersion.getRecommendedVersion(), dependencyVersion) || requestedVersion.containsVersion(dependencyVersion); + } +} diff --git a/src/applaunch/java/org/spongepowered/common/applaunch/plugin/discovery/PluginDiscovery.java b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/discovery/PluginDiscovery.java new file mode 100644 index 00000000000..be10598da23 --- /dev/null +++ b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/discovery/PluginDiscovery.java @@ -0,0 +1,301 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.applaunch.plugin.discovery; + +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.common.applaunch.plugin.PluginPlatformConstants; +import org.spongepowered.common.applaunch.plugin.PluginServiceLoader; +import org.spongepowered.common.applaunch.plugin.VersionChecker; +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.PluginLoader; +import org.spongepowered.plugin.PluginService; +import org.spongepowered.plugin.discovery.*; +import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.metadata.builtin.MetadataParser; +import org.spongepowered.plugin.metadata.model.PluginConflict; + +import java.lang.module.ModuleDescriptor; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class PluginDiscovery extends PluginServiceLoader { + private Map candidates; + private List readers; + + protected PluginDiscovery(final Environment environment) { + super(environment); + } + + public final void discoverPluginResources() { + final Map candidates = new HashMap<>(); + final List readers = new ArrayList<>(); + + final Set locatorClasses = new HashSet<>(); + final Set readerClasses = new HashSet<>(); + + int maxBatches = 10; + final String maxBatchesProp = System.getProperty("sponge.discovery.maxBatches"); + if (maxBatchesProp != null) { + try { + maxBatches = Integer.parseInt(maxBatchesProp); + } catch (final NumberFormatException ignored) {} + } + + int batch = 1; + + while (true) { + this.environment.logger().info("Running discovery batch #{} ...", batch); + + final List batchServices = new ArrayList<>(); + + readers.addAll(this.loadServices("metadata reader", PluginMetadataReader.class, cl -> readerClasses.add(cl.getName()))); + + for (final PluginResourceLocator locator : this.loadServices("resource locator", PluginResourceLocator.class, cl -> locatorClasses.add(cl.getName()))) { + final Collection results; + try { + results = locator.locatePluginResources(this.environment); + } catch (final Exception e) { + this.environment.logger().error("Service '{}' failed to locate plugin resources.", locator.name(), e); + continue; + } + this.environment.logger().info("Service '{}' located {} resources.", locator.name(), results.size()); + + for (final PluginResourceLocator.Result result : results) { + Candidate candidate = candidates.get(result.resource()); + if (candidate == null) { + candidate = new Candidate(result.resource(), result.unknownResourceStrategy()); + candidates.put(result.resource(), candidate); + candidate.detectServices(locatorClasses, readerClasses); + if (candidate.newDiscoveryServiceFound) { + batchServices.add((SpongeJVMPluginResource) result.resource()); + } + } else { + candidate.unknownResourceStrategy = candidate.unknownResourceStrategy.merge(result.unknownResourceStrategy()); + } + candidate.locators.add(locator); + } + } + + this.environment.logger().info("Found {} new discovery services.", batchServices.size()); + if (batchServices.isEmpty()) { + break; + } + + if (++batch > maxBatches) { + this.environment.logger().warn("Max batches reached."); + break; + } + + try { + this.appendDiscoveryServices(batchServices, batch); + } catch (final Exception e) { + this.environment.logger().error("Failed to build new service layer.", e); + break; + } + } + + for (final Candidate candidate : candidates.values()) { + this.environment.logger().debug("Found {} located by [{}] ({}).", candidate.resource, candidate.locatorNames(), candidate.serviceNames()); + } + + this.candidates = candidates; + this.readers = readers; + } + + protected abstract void appendDiscoveryServices(final List resources, final int batch) throws Exception; + + public final Collection candidates() { + return Collections.unmodifiableCollection(this.candidates.values()); + } + + public final Candidate candidate(final PluginResource resource) { + Candidate candidate = this.candidates.get(resource); + if (candidate == null) { + candidate = new Candidate(resource, UnknownResourceStrategy.IGNORE); + this.candidates.put(resource, candidate); + this.environment.logger().debug("Found {} located by the platform.", resource); + } + return candidate; + } + + public final Collection gameResources() { + return this.candidates.values().stream().filter(Candidate::gameResource).map(Candidate::resource).toList(); + } + + public final void logMetadataWarnings() { + for (final String warning : MetadataParser.warnings()) { + this.environment.logger().warn(warning); + } + } + + public final boolean checkConflicts() { + return this.checkConflicts(this.candidates.values().stream().flatMap(c -> c.metadata.values().stream())); + } + + public final boolean checkConflicts(final Stream allMetadata) { + final Map map = new LinkedHashMap<>(); + allMetadata.forEachOrdered(m -> map.put(m.id(), m)); + + boolean anyFatal = false; + for (final PluginMetadata metadata : map.values()) { + for (final PluginConflict conflict : metadata.conflicts()) { + final @Nullable PluginMetadata target = map.get(conflict.id()); + if (target != null && VersionChecker.check(conflict.version(), target.version())) { + if (conflict.fatal() && !PluginPlatformConstants.IGNORE_FATAL_CONFLICTS) { + this.environment.logger().fatal("Plugin {}={} is fatally conflicting with {}={} in range {}. Reason: {}", + metadata.id(), metadata.version(), target.id(), target.version(), conflict.version(), conflict.reason().orElse("?")); + anyFatal = true; + } else { + this.environment.logger().warn("Plugin {}={} is conflicting with {}={} in range {}. Reason: {}", + metadata.id(), metadata.version(), target.id(), target.version(), conflict.version(), conflict.reason().orElse("?")); + } + } + } + } + + return anyFatal; + } + + public final class Candidate { + private final PluginResource resource; + private final List locators = new ArrayList<>(); + private @MonotonicNonNull UnknownResourceStrategy unknownResourceStrategy; + private final SequencedMap metadata = new LinkedHashMap<>(); + private boolean locatorFound, readerFound, loaderFound, modFound; + private boolean newDiscoveryServiceFound; + + public Candidate(final PluginResource resource, final UnknownResourceStrategy unknownResourceStrategy) { + this.resource = resource; + this.unknownResourceStrategy = unknownResourceStrategy; + } + + public PluginResource resource() { + return this.resource; + } + + private void detectServices(final Set existingLocators, final Set existingReaders) { + if (this.resource instanceof SpongeJVMPluginResource jvmResource) { + final ModuleDescriptor descriptor; + try { + descriptor = jvmResource.module(); + } catch (Exception ex) { + PluginDiscovery.this.environment.logger().warn("Cannot read module descriptor of {}", jvmResource, ex); + return; + } + + final Map> providers = new HashMap<>(); + for (final ModuleDescriptor.Provides provides : descriptor.provides()) { + providers.put(provides.service(), provides.providers()); + } + final List locators = providers.getOrDefault(PluginResourceLocator.class.getName(), Collections.emptyList()); + final List readers = providers.getOrDefault(PluginMetadataReader.class.getName(), Collections.emptyList()); + final List loaders = providers.getOrDefault(PluginLoader.class.getName(), Collections.emptyList()); + this.locatorFound = !locators.isEmpty(); + this.readerFound = !readers.isEmpty(); + this.loaderFound = !loaders.isEmpty(); + this.newDiscoveryServiceFound = locators.stream().anyMatch(key -> !existingLocators.contains(key)) || readers.stream().anyMatch(key -> !existingReaders.contains(key)); + } + } + + private String serviceNames() { + final StringJoiner joiner = new StringJoiner(", "); + if (this.locatorFound) { + joiner.add("locator"); + } + if (this.readerFound) { + joiner.add("reader"); + } + if (this.loaderFound) { + joiner.add("loader"); + } + return joiner.toString(); + } + + private String locatorNames() { + return this.locators.stream().map(PluginService::name).collect(Collectors.joining(", ")); + } + + public List locators() { + return Collections.unmodifiableList(this.locators); + } + + public void readMetadata() { + this.metadata.clear(); + for (final PluginMetadataReader reader : PluginDiscovery.this.readers) { + final Collection plugins; + try { + plugins = reader.readPluginMetadata(PluginDiscovery.this.environment, this.resource, this.locators()); + } catch (final Exception e) { + PluginDiscovery.this.environment.logger().error("Service '{}' failed to read plugin metadata", reader.name(), e); + continue; + } + for (final PluginMetadata plugin : plugins) { + this.metadata.put(plugin.id(), plugin); + } + } + } + + public SequencedCollection metadata() { + return Collections.unmodifiableSequencedCollection(this.metadata.sequencedValues()); + } + + public boolean pluginFound() { + return !this.metadata.isEmpty(); + } + + public boolean gameResource() { + if (this.pluginFound() || this.loaderFound) { + return true; + } + if (this.locatorFound || this.readerFound) { + return false; + } + return this.unknownResourceStrategy.load(); + } + + public void setModFound() { + this.modFound = true; + } + + public void logResult() { + if (this.pluginFound()) { + PluginDiscovery.this.environment.logger().debug("Found {} metadata in {}.", this.metadata.size(), this.resource); + return; + } + if (this.locatorFound || this.readerFound || this.loaderFound || this.modFound) { + return; + } + + final String result = this.unknownResourceStrategy.load() ? "loaded as a game library" : "ignored"; + if (this.unknownResourceStrategy.warn()) { + PluginDiscovery.this.environment.logger().warn("The unknown resource {} will be {}.", this.resource, result); + } else { + PluginDiscovery.this.environment.logger().debug("The unknown resource {} will be {}.", this.resource, result); + } + } + } +} diff --git a/src/main/java/org/spongepowered/common/datapack/DataPackEncoder.java b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/discovery/SpongeJVMPluginResource.java similarity index 82% rename from src/main/java/org/spongepowered/common/datapack/DataPackEncoder.java rename to src/applaunch/java/org/spongepowered/common/applaunch/plugin/discovery/SpongeJVMPluginResource.java index 2b74cd51dfb..bd25b2d6f0e 100644 --- a/src/main/java/org/spongepowered/common/datapack/DataPackEncoder.java +++ b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/discovery/SpongeJVMPluginResource.java @@ -22,13 +22,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.datapack; +package org.spongepowered.common.applaunch.plugin.discovery; -import net.minecraft.core.RegistryAccess; -import org.spongepowered.api.datapack.DataPackEntry; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; -public interface DataPackEncoder> { +import java.lang.module.ModuleDescriptor; - E encode(T serializable, RegistryAccess registryAccess); +public interface SpongeJVMPluginResource extends JVMPluginResource { + ModuleDescriptor module(); } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/locator/SpongePropertyLocatorService.java b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/discovery/SpongePropertyResourceLocator.java similarity index 53% rename from vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/locator/SpongePropertyLocatorService.java rename to src/applaunch/java/org/spongepowered/common/applaunch/plugin/discovery/SpongePropertyResourceLocator.java index 19e5d2e8b4e..7174ba058e2 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/locator/SpongePropertyLocatorService.java +++ b/src/applaunch/java/org/spongepowered/common/applaunch/plugin/discovery/SpongePropertyResourceLocator.java @@ -22,23 +22,21 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.applaunch.plugin.locator; +package org.spongepowered.common.applaunch.plugin.discovery; import org.spongepowered.plugin.Environment; import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; -import org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResourceLocatorService; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.discovery.UnknownResourceStrategy; import java.io.File; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; import java.nio.file.Path; +import java.util.Collection; import java.util.HashSet; -import java.util.Map; import java.util.Set; import java.util.stream.Stream; -public final class SpongePropertyLocatorService implements JVMPluginResourceLocatorService { +public final class SpongePropertyResourceLocator implements PluginResourceLocator { @Override public String name() { @@ -46,46 +44,21 @@ public String name() { } @Override - public Set locatePluginResources(final Environment environment) { - environment.logger().info("Locating '{}' resources...", this.name()); - - final Set resources = new HashSet<>(); + public Collection locatePluginResources(final Environment environment) { + final Set resources = new HashSet<>(); final String resourcesProp = System.getProperty("sponge.resources"); if (resourcesProp != null) { + final UnknownResourceStrategy strategy = new UnknownResourceStrategy(false, true); + for (final String entry : resourcesProp.split(File.pathSeparator)) { if (entry.isBlank()) { continue; } final Path[] paths = Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new); - resources.add(JVMPluginResource.create(environment, this.name(), paths)); - } - } - - final String fsProp = System.getProperty("sponge.rootJarFS"); - if (fsProp != null) { - try { - final FileSystem fs = FileSystems.getFileSystem(new URI(fsProp)); - final Path spongeMod = newJarInJar(fs.getPath("jars", "spongevanilla-mod.jar")); - resources.add(JVMPluginResource.create(environment, this.name(), spongeMod)); - } catch (final Exception e) { - environment.logger().error("Failed to locate spongevanilla-mod jar."); + resources.add(new Result(JVMPluginResource.create(environment, paths), strategy)); } } - - environment.logger().info("Located [{}] resource(s) for '{}'...", resources.size(), this.name()); - return resources; } - - private static Path newJarInJar(final Path jar) { - try { - URI jij = new URI("jij:" + jar.toAbsolutePath().toUri().getRawSchemeSpecificPart()).normalize(); - final Map env = Map.of("packagePath", jar); - FileSystem jijFS = FileSystems.newFileSystem(jij, env); - return jijFS.getPath("/"); // root of the archive to load - } catch (Exception e) { - throw new RuntimeException(e); - } - } } diff --git a/forge/src/main/java/org/spongepowered/forge/SpongeMod.java b/src/applaunch/java/org/spongepowered/common/applaunch/test/TestEnvironment.java similarity index 86% rename from forge/src/main/java/org/spongepowered/forge/SpongeMod.java rename to src/applaunch/java/org/spongepowered/common/applaunch/test/TestEnvironment.java index 3785f28e1e5..be3685b47e8 100644 --- a/forge/src/main/java/org/spongepowered/forge/SpongeMod.java +++ b/src/applaunch/java/org/spongepowered/common/applaunch/test/TestEnvironment.java @@ -22,10 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge; +package org.spongepowered.common.applaunch.test; -import net.minecraftforge.fml.common.Mod; +public class TestEnvironment { -@Mod("sponge") -public class SpongeMod { + public static boolean isActive() { + return "true".equals(System.getProperty("sponge.test.active")); + } } diff --git a/src/applaunch/java/org/spongepowered/common/applaunch/transformation/AccessWidenerTransformer.java b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/AccessWidenerTransformer.java new file mode 100644 index 00000000000..b47590b480a --- /dev/null +++ b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/AccessWidenerTransformer.java @@ -0,0 +1,91 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.applaunch.transformation; + +import net.fabricmc.accesswidener.AccessWidener; +import net.fabricmc.accesswidener.AccessWidenerClassVisitor; +import net.fabricmc.accesswidener.AccessWidenerReader; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Set; + +public abstract class AccessWidenerTransformer { + public static final String NAME = "access_widener"; + public static final String EXTENSION = "accesswidener"; + public static final String MANIFEST_ATTRIBUTE = "Access-Widener"; + + protected static final Logger LOGGER = LogManager.getLogger(); + + private @MonotonicNonNull AccessWidener widener; + + protected final ClassNode transform(final ClassNode input) { + if (this.widener == null) { + throw new IllegalStateException("Resources not loaded"); + } + AccessWidenerTransformer.LOGGER.debug("Transforming {}", input.name); + final ClassNode output = new ClassNode(Opcodes.ASM9); + final ClassVisitor visitor = AccessWidenerClassVisitor.createClassVisitor(Opcodes.ASM9, output, this.widener); + input.accept(visitor); + return output; + } + + protected abstract Collection collectResources(); + + public final synchronized void loadResources() { + if (this.widener == null) { + this.widener = new AccessWidener(); + final AccessWidenerReader awReader = new AccessWidenerReader(this.widener); + + for (final URL resource : this.collectResources()) { + if (resource.getFile().endsWith(AccessWidenerTransformer.EXTENSION)) { + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream(), StandardCharsets.UTF_8))) { + AccessWidenerTransformer.LOGGER.debug("Reading access widener {}", resource); + awReader.read(reader); + } catch (final IOException ex) { + AccessWidenerTransformer.LOGGER.error("Failed to load access widener {}", resource, ex); + } + } else { + AccessWidenerTransformer.LOGGER.warn("Offered access widener {} that does not end with expected extension '{}'", resource, AccessWidenerTransformer.EXTENSION); + } + } + } + } + + protected final Set targetClasses() { + this.loadResources(); + return this.widener.getTargets(); + } +} diff --git a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/ListenerTransformerHelper.java b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/ListenerTransformer.java similarity index 71% rename from modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/ListenerTransformerHelper.java rename to src/applaunch/java/org/spongepowered/common/applaunch/transformation/ListenerTransformer.java index 7b65503bfa1..1b5ff36aae9 100644 --- a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/ListenerTransformerHelper.java +++ b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/ListenerTransformer.java @@ -22,12 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.transformers.modlauncher; - -import static org.objectweb.asm.Opcodes.ACC_STATIC; -import static org.objectweb.asm.Opcodes.INVOKESTATIC; -import static org.objectweb.asm.Opcodes.RETURN; +package org.spongepowered.common.applaunch.transformation; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; @@ -37,14 +34,15 @@ import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; -public class ListenerTransformerHelper { - public static final String LISTENER_DESC = "Lorg/spongepowered/api/event/Listener;"; +public class ListenerTransformer { + public static final String NAME = "listener"; + public static final String ANNOTATION_DESC = "Lorg/spongepowered/api/event/Listener;"; - public static boolean shouldTransform(ClassNode classNode) { + public final boolean shouldTransform(final ClassNode classNode) { for (final MethodNode method : classNode.methods) { if (method.visibleAnnotations != null) { for (final AnnotationNode annotation : method.visibleAnnotations) { - if (annotation.desc.equals(LISTENER_DESC)) { + if (annotation.desc.equals(ANNOTATION_DESC)) { return true; } } @@ -53,7 +51,7 @@ public static boolean shouldTransform(ClassNode classNode) { return false; } - public static void transform(ClassNode classNode) { + public final void transform(final ClassNode classNode) { MethodNode clinit = null; for (final MethodNode method : classNode.methods) { if (method.name.equals("") && method.desc.equals("()V")) { @@ -63,16 +61,16 @@ public static void transform(ClassNode classNode) { } if (clinit == null) { - clinit = new MethodNode(ACC_STATIC, "", "()V", null, null); - clinit.instructions.add(new InsnNode(RETURN)); + clinit = new MethodNode(Opcodes.ACC_STATIC, "", "()V", null, null); + clinit.instructions.add(new InsnNode(Opcodes.RETURN)); classNode.methods.add(clinit); } final InsnList list = new InsnList(); list.add(new LdcInsnNode(Type.getObjectType(classNode.name))); - list.add(new MethodInsnNode(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false)); - list.add(new MethodInsnNode(INVOKESTATIC, "org/spongepowered/common/event/ListenerLookups", "set", "(Ljava/lang/Class;Ljava/lang/invoke/MethodHandles$Lookup;)V")); + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false)); + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "org/spongepowered/common/event/ListenerLookups", "set", "(Ljava/lang/Class;Ljava/lang/invoke/MethodHandles$Lookup;)V")); clinit.instructions.insert(list); } diff --git a/src/applaunch/java/org/spongepowered/common/applaunch/transformation/SuperclassChangeTransformer.java b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/SuperclassChangeTransformer.java new file mode 100644 index 00000000000..bc18b0d6ab8 --- /dev/null +++ b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/SuperclassChangeTransformer.java @@ -0,0 +1,136 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.applaunch.transformation; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public abstract class SuperclassChangeTransformer { + public static final String NAME = "superclass_change"; + public static final String EXTENSION = "superclasschange"; + public static final String MANIFEST_ATTRIBUTE = "Superclass-Transformer"; + + protected static final Logger LOGGER = LogManager.getLogger(); + + private @MonotonicNonNull Map superclassMap; + + private static Map readMap(final URL resource) throws IOException { + final Map targets = new HashMap<>(); + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (!line.isEmpty()) { + String[] parts = line.split(":", 2); + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid map entry: " + line); + } + targets.put(parts[0], parts[1]); + } + } + } + return targets; + } + + protected abstract Collection collectResources(); + + public final synchronized void loadResources() { + if (this.superclassMap == null) { + this.superclassMap = new ConcurrentHashMap<>(); + + for (final URL resource : this.collectResources()) { + if (resource.getFile().endsWith(SuperclassChangeTransformer.EXTENSION)) { + try { + SuperclassChangeTransformer.LOGGER.debug("Reading superclass change {}", resource); + this.superclassMap.putAll(SuperclassChangeTransformer.readMap(resource)); + } catch (final Exception ex) { + SuperclassChangeTransformer.LOGGER.error("Failed to load superclass change {}", resource, ex); + } + } else { + SuperclassChangeTransformer.LOGGER.warn("Offered superclass change {} that does not end with expected extension '{}'", resource, SuperclassChangeTransformer.EXTENSION); + } + } + } + } + + protected final boolean transform(final ClassNode classNode) { + if (this.superclassMap == null) { + throw new IllegalStateException("Resources not loaded"); + } + + final String inputKey = classNode.name.replace("/", "."); + final String newSuperclass = this.superclassMap.get(inputKey); + if (newSuperclass == null) { + SuperclassChangeTransformer.LOGGER.warn("No superclass change for {}", inputKey); + return false; + } + + final String sanitizedSuperClass = newSuperclass.replace('.', '/'); + classNode.methods.forEach(m -> SuperclassChangeTransformer.transformMethod(m, classNode.superName, sanitizedSuperClass)); + classNode.superName = sanitizedSuperClass; + return true; + } + + private static void transformMethod(final MethodNode node, final String originalSuperclass, final String superClass) { + for (final MethodInsnNode insn : SuperclassChangeTransformer.findSuper(node, originalSuperclass)) { + insn.owner = superClass; + } + } + + private static List findSuper(final MethodNode method, final String originalSuperClass) { + final List nodes = new ArrayList<>(); + for (final AbstractInsnNode node : method.instructions.toArray()) { + if (node.getOpcode() == Opcodes.INVOKESPECIAL && originalSuperClass.equals( + ((MethodInsnNode) node).owner)) { + nodes.add((MethodInsnNode) node); + } + } + return nodes; + } + + protected final Set targetClasses() { + this.loadResources(); + return this.superclassMap.keySet(); + } +} diff --git a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/AgentAccessGenerator.java b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/AgentAccessGenerator.java similarity index 96% rename from modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/AgentAccessGenerator.java rename to src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/AgentAccessGenerator.java index d4b2c8a9698..9f0232396a7 100644 --- a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/AgentAccessGenerator.java +++ b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/AgentAccessGenerator.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.transformers.modlauncher.jacoco; +package org.spongepowered.common.applaunch.transformation.jacoco; import org.jacoco.core.runtime.IExecutionDataAccessorGenerator; import org.jacoco.core.runtime.RuntimeData; diff --git a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/Instrumenter.java b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/Instrumenter.java similarity index 98% rename from modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/Instrumenter.java rename to src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/Instrumenter.java index 3c9457f54bc..58540247c3f 100644 --- a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/Instrumenter.java +++ b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/Instrumenter.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.transformers.modlauncher.jacoco; +package org.spongepowered.common.applaunch.transformation.jacoco; import org.jacoco.core.internal.data.CRC64; import org.jacoco.core.internal.flow.ClassProbesAdapter; diff --git a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/JacocoPluginService.java b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/JacocoTransformer.java similarity index 78% rename from modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/JacocoPluginService.java rename to src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/JacocoTransformer.java index e8c176b047b..b9de381068e 100644 --- a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/JacocoPluginService.java +++ b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/JacocoTransformer.java @@ -22,15 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.transformers.modlauncher.jacoco; +package org.spongepowered.common.applaunch.transformation.jacoco; -import cpw.mods.modlauncher.TransformingClassLoader; -import cpw.mods.modlauncher.api.ITransformerActivity; -import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.objectweb.asm.ClassReader; -import org.objectweb.asm.Type; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; @@ -39,22 +35,19 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.EnumSet; import java.util.List; -public class JacocoPluginService implements ILaunchPluginService { - private static final Logger logger = LogManager.getLogger(); - +public abstract class JacocoTransformer { + public static final String NAME = "jacoco"; private static final String MIXIN_DESC = "Lorg/spongepowered/asm/mixin/Mixin;"; - private static final EnumSet YAY = EnumSet.of(Phase.BEFORE); - private static final EnumSet NAY = EnumSet.noneOf(Phase.class); + private static final Logger LOGGER = LogManager.getLogger(); - private boolean disabled; + protected boolean disabled; private String[] packages; private ClassLoader loader; - public JacocoPluginService() { + public JacocoTransformer() { try { getClass().getClassLoader().loadClass("org.jacoco.core.JaCoCo"); } catch (final ClassNotFoundException e) { @@ -71,16 +64,6 @@ public JacocoPluginService() { } } - @Override - public String name() { - return "jacoco"; - } - - @Override - public EnumSet handlesClass(Type classType, boolean isEmpty) { - return this.disabled || isEmpty ? NAY : YAY; - } - private boolean testClassName(final String name) { for (final String pkg : this.packages) { if (name.startsWith(pkg)) { @@ -90,17 +73,18 @@ private boolean testClassName(final String name) { return false; } - @Override - public int processClassWithFlags(final Phase phase, final ClassNode classNode, final Type classType, final String reason) { - if (ITransformerActivity.COMPUTING_FRAMES_REASON.equals(reason)) { - return ComputeFlags.NO_REWRITE; + protected abstract boolean isTransformingClassloader(final ClassLoader loader); + + protected boolean transform(final ClassNode classNode) { + if (this.disabled) { + return false; } // Capture a reference to the transforming class loader if (this.loader == null) { // First transformed class context, aka game entrypoint is supposed to be the transforming class loader final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); - if (!((Object) contextLoader instanceof TransformingClassLoader)) { // Ignore IDE warning + if (!isTransformingClassloader(contextLoader)) { throw new IllegalStateException("Context class loader of the first transformed class is not a transforming class loader"); } // Context class loader may change for next classes, so capture it @@ -111,7 +95,7 @@ public int processClassWithFlags(final Phase phase, final ClassNode classNode, f // - Performance // - JaCoCo changes the LVT which can cause mixins targeting the current class to fail if (!this.testClassName(classNode.name)) { - return ComputeFlags.NO_REWRITE; + return false; } // We need the original bytes for two reasons: @@ -120,16 +104,16 @@ public int processClassWithFlags(final Phase phase, final ClassNode classNode, f byte[] originalBytes; try (final InputStream in = this.loader.getResourceAsStream(classNode.name + ".class")) { if (in == null) { - logger.warn("Failed to find original class bytes for class {}", classNode.name); - return ComputeFlags.NO_REWRITE; + LOGGER.warn("Failed to find original class bytes for class {}", classNode.name); + return false; } final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); in.transferTo(buffer); originalBytes = buffer.toByteArray(); } catch (IOException e) { - logger.warn("Failed to read original class bytes for class {}", classNode.name, e); - return ComputeFlags.NO_REWRITE; + LOGGER.warn("Failed to read original class bytes for class {}", classNode.name, e); + return false; } // Detect the mixin annotation @@ -168,9 +152,7 @@ public int processClassWithFlags(final Phase phase, final ClassNode classNode, f classNode.methods = instrumentedClassNode.methods; } - // JaCoCo has already updated the frames - return ComputeFlags.SIMPLE_REWRITE; + return true; } - } diff --git a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/PrefixedCondyProbeArrayStrategy.java b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/PrefixedCondyProbeArrayStrategy.java similarity index 97% rename from modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/PrefixedCondyProbeArrayStrategy.java rename to src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/PrefixedCondyProbeArrayStrategy.java index 3d38c30951a..f1f1fa5599f 100644 --- a/modlauncher-transformers/src/main/java/org/spongepowered/transformers/modlauncher/jacoco/PrefixedCondyProbeArrayStrategy.java +++ b/src/applaunch/java/org/spongepowered/common/applaunch/transformation/jacoco/PrefixedCondyProbeArrayStrategy.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.transformers.modlauncher.jacoco; +package org.spongepowered.common.applaunch.transformation.jacoco; import org.jacoco.core.internal.instr.CondyProbeArrayStrategy; import org.jacoco.core.internal.instr.IProbeArrayStrategy; diff --git a/src/main/java/org/spongepowered/common/SpongeCommon.java b/src/main/java/org/spongepowered/common/SpongeCommon.java index b6e35653291..ba98f96dba4 100644 --- a/src/main/java/org/spongepowered/common/SpongeCommon.java +++ b/src/main/java/org/spongepowered/common/SpongeCommon.java @@ -38,12 +38,13 @@ import org.spongepowered.api.Sponge; import org.spongepowered.api.event.Event; import org.spongepowered.common.applaunch.AppLaunch; +import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.launch.Launch; import org.spongepowered.common.launch.config.core.SpongeConfigs; +import org.spongepowered.common.registry.SpongeRegistryHolder; import org.spongepowered.common.scheduler.AsyncScheduler; import org.spongepowered.common.scheduler.ServerScheduler; -import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -52,28 +53,15 @@ public final class SpongeCommon { private static final Logger LOGGER = LogManager.getLogger(Launch.instance().id()); private static final SpongeMinecraftVersion MINECRAFT_VERSION = new SpongeMinecraftVersion( - SharedConstants.getCurrentVersion().getName(), - SharedConstants.getCurrentVersion().getId(), - SharedConstants.getCurrentVersion().getProtocolVersion() + SharedConstants.getCurrentVersion().name(), + SharedConstants.getCurrentVersion().id(), + SharedConstants.getCurrentVersion().protocolVersion() ); @Inject private @Nullable static SpongeGame game; private static @MonotonicNonNull Path pluginConfigDir; - static { - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - final @Nullable SpongeGame game = SpongeCommon.game; - if (game != null) { - try { - game.configManager().close(); - } catch (final IOException e) { - SpongeCommon.logger().error("Failed to shut down configuration watch service", e); - } - } - }, "Sponge shutdown thread")); - } - private SpongeCommon() { } @@ -105,8 +93,20 @@ public static RegistryAccess.Frozen vanillaRegistryAccess() { return SpongeCommon.server().registryAccess(); } + public static SpongeRegistryHolder scopedHolder() { + var holder = PhaseTracker.getInstance().startupRegistryHolder(); + // If we have a holder in scope use it directly unless it is game + if (holder == null) { + if (SpongeCommon.game().isServerAvailable()) { + return (SpongeRegistryHolder) SpongeCommon.server(); + } + return SpongeCommon.game(); + } + return holder; + } + public static Registry vanillaRegistry(ResourceKey> key) { - return SpongeCommon.vanillaRegistryAccess().registryOrThrow(key); + return (Registry) SpongeCommon.scopedHolder().lookupOrThrow(key); } public static ServerScheduler serverScheduler() { diff --git a/src/main/java/org/spongepowered/common/SpongeLifecycle.java b/src/main/java/org/spongepowered/common/SpongeLifecycle.java index cd05f470768..feaf6141197 100644 --- a/src/main/java/org/spongepowered/common/SpongeLifecycle.java +++ b/src/main/java/org/spongepowered/common/SpongeLifecycle.java @@ -29,11 +29,10 @@ import com.google.inject.Singleton; import io.leangen.geantyref.TypeToken; import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.RegistryLayer; import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.permissions.PermissionLevel; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.level.WorldDataConfiguration; import org.checkerframework.checker.nullness.qual.Nullable; @@ -42,16 +41,20 @@ import org.spongepowered.api.Game; import org.spongepowered.api.Server; import org.spongepowered.api.Sponge; -import org.spongepowered.api.advancement.criteria.trigger.Trigger; import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.EventContext; import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.common.applaunch.plugin.DummyPluginContainer; -import org.spongepowered.common.bridge.server.MinecraftServerBridge; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryRoots; +import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.common.applaunch.plugin.PluginPlatform; +import org.spongepowered.common.bridge.core.WritableRegistryBridge; import org.spongepowered.common.bridge.server.packs.resources.ResourceManagerBridge; import org.spongepowered.common.data.SpongeDataManager; import org.spongepowered.common.event.lifecycle.AbstractRegisterRegistryEvent; import org.spongepowered.common.event.lifecycle.AbstractRegisterRegistryValueEvent; +import org.spongepowered.common.event.lifecycle.FreezeRegistryEventImpl; import org.spongepowered.common.event.lifecycle.RegisterBuilderEventImpl; import org.spongepowered.common.event.lifecycle.RegisterChannelEventImpl; import org.spongepowered.common.event.lifecycle.RegisterDataEventImpl; @@ -60,6 +63,7 @@ import org.spongepowered.common.event.manager.SpongeEventManager; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.launch.Lifecycle; +import org.spongepowered.common.launch.plugin.SpongePluginContainer; import org.spongepowered.common.network.channel.SpongeChannelManager; import org.spongepowered.common.profile.SpongeGameProfileManager; import org.spongepowered.common.registry.SpongeBuilderProvider; @@ -68,13 +72,16 @@ import org.spongepowered.common.registry.SpongeRegistryHolder; import org.spongepowered.common.scheduler.AsyncScheduler; import org.spongepowered.common.service.SpongeServiceProvider; +import org.spongepowered.common.service.server.SpongeServerScopedServiceProvider; import org.spongepowered.common.service.server.permission.SpongeContextCalculator; import org.spongepowered.common.util.ExecutorUtil; import org.spongepowered.common.world.server.SpongeWorldManager; import org.spongepowered.plugin.PluginContainer; -import java.util.Collection; +import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; @Singleton public final class SpongeLifecycle implements Lifecycle { @@ -82,7 +89,6 @@ public final class SpongeLifecycle implements Lifecycle { private final Game game; private final Injector injector; private FeatureFlagSet featureFlags; - public boolean establishedPluginRegistries = false; @Inject public SpongeLifecycle(final Game game, final Injector injector) { @@ -116,50 +122,38 @@ public void callRegisterBuilderEvent() { this.game.eventManager().post(new RegisterBuilderEventImpl(Cause.of(EventContext.empty(), this.game), this.game)); } + @SuppressWarnings("unchecked") @Override public void establishEarlyGlobalRegistries() { final SpongeRegistryHolder holder = (SpongeRegistryHolder) this.game; holder.setRootMinecraftRegistry((Registry>) BuiltInRegistries.REGISTRY); + holder.streamRegistries().forEach(r -> ((WritableRegistryBridge) r).bridge$setRegistryHolder(this.game)); SpongeRegistries.registerEarlyGlobalRegistries(holder); } @Override - public void finalizeEarlyGlobalRegistries() { + public void establishGlobalRegistries() { + final SpongeRegistryHolder holder = (SpongeRegistryHolder) this.game; + + // Plugin registries + this.game.eventManager().post(new AbstractRegisterRegistryEvent.GameScopedImpl(Cause.of(EventContext.empty(), this.game), this.game)); + + // Freeze Sponge Root - Registries are now available + holder.registryHolder().freezeSpongeRootRegistry(); + + final Map, org.spongepowered.api.registry.Registry> map = + holder.streamRegistries().collect(Collectors.toMap(org.spongepowered.api.registry.Registry::type, Function.identity())); - // Vanilla registries we want plugins to be able to modify: - // TODO marker for this in API? - this.game.eventManager().post(new AbstractRegisterRegistryValueEvent.BuiltInImpl<>(Cause.of(EventContext.empty(), this.game), this.game, - (org.spongepowered.api.registry.Registry>) BuiltInRegistries.TRIGGER_TYPES)); + this.game.eventManager().post(new AbstractRegisterRegistryValueEvent.GameScopedImpl(Cause.of(EventContext.empty(), this.game), this.game, map)); + map.values().forEach(r -> ((WritableRegistryBridge) r).bridge$markEventCalled()); + holder.registryHolder().freezeSpongeDynamicRegistries(false); } - @Override - public void establishGlobalRegistries(final RegistryAccess.Frozen registryAccess, final RegistryLayer layer) { - final SpongeRegistryHolder holder = (SpongeRegistryHolder) this.game; - SpongeCommon.logger().info("Layer {}", layer); - switch (layer) - { - // WORLDGEN -> - case DIMENSIONS -> { - SpongeRegistries.registerGlobalRegistriesDimensionLayer((SpongeRegistryHolder) this.game, registryAccess, this.featureFlags); - - // Plugin registries - this.game.eventManager().post(new AbstractRegisterRegistryEvent.GameScopedImpl(Cause.of(EventContext.empty(), this.game), this.game)); - - // Freeze Sponge Root - Registries are now available - holder.registryHolder().freezeSpongeRootRegistry(); - } - case RELOADABLE -> { - if (!this.establishedPluginRegistries) { - // Plugin registry values - this.game.eventManager().post(new AbstractRegisterRegistryValueEvent.GameScopedImpl(Cause.of(EventContext.empty(), this.game), this.game)); - // Freeze Dynamic Registries - Values are now available - holder.registryHolder().freezeSpongeDynamicRegistries(); - - this.establishedPluginRegistries = true; - } - } - } + public void endEstablishGlobalRegistries() { + ((SpongeRegistryHolder) this.game).registryHolder().freezeSpongeDynamicRegistries(true); + + this.game.eventManager().post(new FreezeRegistryEventImpl.PostImpl.GameImpl(Cause.of(EventContext.empty(), this.game), this.game)); } @Override @@ -190,13 +184,22 @@ public void establishGameServices() { } @Override - public void establishServerServices() { - ((MinecraftServerBridge) this.game.server()).bridge$initServices(this.game, this.injector); + public void establishServerServices(final ResourceManager resourceManager, final PermissionLevel functionsPermissionLevel) { + final SpongeServerScopedServiceProvider serviceProvider = new SpongeServerScopedServiceProvider(this.game, this.injector, functionsPermissionLevel); + serviceProvider.init(); + ((ResourceManagerBridge) resourceManager).bridge$services(serviceProvider); } @Override public void establishServerFeatures() { - Sponge.server().serviceProvider().contextService().registerContextCalculator(new SpongeContextCalculator()); + // In 26.1, the service provider may not yet be available during ServerAboutToStartEvent + // on the integrated server path (NeoForge/Forge). The service provider is set in + // MinecraftServer. TAIL from the WorldStem's resource manager, but on integrated + // servers the event can fire before the services propagate. Guard against this. + final var serviceProvider = Sponge.server().serviceProvider(); + if (serviceProvider != null) { + serviceProvider.contextService().registerContextCalculator(new SpongeContextCalculator()); + } // Yes this looks odd but prevents having to do sided lifecycle solely to always point at the Server ((SpongeServer) this.game.server()).getUsernameCache().load(); } @@ -205,42 +208,77 @@ public void establishServerFeatures() { @Override public void callConstructEvent() { - for (final PluginContainer plugin : this.filterInternalPlugins(this.game.pluginManager().plugins())) { + for (final PluginContainer plugin : this.game.pluginManager().plugins()) { ((SpongeEventManager) this.game.eventManager()).postToPlugin(SpongeEventFactory.createConstructPluginEvent(Cause.of(EventContext.empty(), this.game), this.game, plugin), plugin); } } @Override - public void establishServerRegistries(final Server server) { - SpongeRegistries.registerServerRegistries(server); + public void beginEstablishServerRegistries(final RegistryHolder server) { + SpongeRegistries.registerServerRegistries((SpongeRegistryHolder) server, this.featureFlags); + + ((SpongeRegistryHolder) server).registryHolder().featureFlagSet(this.featureFlags); - this.game.eventManager().post(new AbstractRegisterRegistryEvent.EngineScopedImpl<>(Cause.of(EventContext.empty(), this.game), this.game, - server)); + this.game.eventManager().post( + AbstractRegisterRegistryEvent.EngineScopedImpl.server(Cause.of(EventContext.empty(), this.game), this.game, server)); ((SpongeRegistryHolder) server).registryHolder().freezeSpongeRootRegistry(); - this.game.eventManager().post(new AbstractRegisterRegistryValueEvent.EngineScopedImpl<>(Cause.of(EventContext.empty(), this.game), - this.game, server)); + this.processServerRegistries(server, server.streamRegistries(RegistryRoots.SPONGE) + .filter(r -> !r.type().equals(RegistryTypes.ADVANCEMENT) && !r.type().equals(RegistryTypes.RECIPE))); + } - ((SpongeRegistryHolder) server).registryHolder().freezeSpongeDynamicRegistries(); + @Override + public void processServerRegistries(final RegistryHolder server, final Stream> registries) { + try { + PhaseTracker.getInstance().startupRegistryHolder(server); + final Map, org.spongepowered.api.registry.Registry> map = + registries.collect(Collectors.toMap(org.spongepowered.api.registry.Registry::type, Function.identity())); + if (!map.isEmpty()) { + map.values().forEach(r -> ((WritableRegistryBridge) r).bridge$setRegistryHolder(server)); + this.game.eventManager().post(AbstractRegisterRegistryValueEvent.EngineScopedImpl.server(Cause.of(EventContext.empty(), this.game), this.game, server, map)); + map.values().forEach(r -> ((WritableRegistryBridge) r).bridge$markEventCalled()); + ((SpongeRegistryHolder) server).registryHolder().freezeSpongeDynamicRegistries(false); + } + } finally { + PhaseTracker.getInstance().startupRegistryHolder(null); + } + } + + @Override + public void endEstablishServerRegistries(final RegistryHolder server) { + ((SpongeRegistryHolder) server).registryHolder().freezeSpongeDynamicRegistries(true); } @Override public void establishClientRegistries(final Client client) { - this.game.eventManager().post(new AbstractRegisterRegistryEvent.EngineScopedImpl<>(Cause.of(EventContext.empty(), this.game), this.game, - client)); + this.game.eventManager().post( + AbstractRegisterRegistryEvent.EngineScopedImpl.client(Cause.of(EventContext.empty(), this.game), this.game, client)); ((SpongeRegistryHolder) client).registryHolder().freezeSpongeRootRegistry(); - this.game.eventManager().post(new AbstractRegisterRegistryValueEvent.EngineScopedImpl<>(Cause.of(EventContext.empty(), this.game), - this.game, client)); + this.game.eventManager().post(AbstractRegisterRegistryValueEvent.EngineScopedImpl.client(Cause.of(EventContext.empty(), this.game), + this.game, client, client.streamRegistries(RegistryRoots.SPONGE).collect(Collectors.toMap(org.spongepowered.api.registry.Registry::type, Function.identity())))); + + ((SpongeRegistryHolder) client).registryHolder().freezeSpongeDynamicRegistries(true); - ((SpongeRegistryHolder) client).registryHolder().freezeSpongeDynamicRegistries(); + this.game.eventManager().post(FreezeRegistryEventImpl.PostImpl.EngineImpl.client(Cause.of(EventContext.empty(), this.game), this.game, client)); } @Override public void callStartingEngineEvent(final Engine engine) { + final boolean server = engine instanceof Server; + for (final PluginContainer container : this.game.pluginManager().plugins()) { + if (container instanceof SpongePluginContainer spongeContainer) { + try { + spongeContainer.loadSidedEntrypoints(server); + } catch (final Exception e) { + PluginPlatform.LOGGER.error("Failed to load {} sided entrypoints of plugin {}", server ? "server" : "client", container.metadata().id(), e); + } + } + } + this.game.eventManager().post(SpongeEventFactory.createStartingEngineEvent(PhaseTracker.getInstance().currentCause(), engine, this.game, (TypeToken) TypeToken.get(engine.getClass()))); } @@ -285,13 +323,6 @@ public void setWorldDataConfiguration(final WorldDataConfiguration config) { this.featureFlags = config.enabledFeatures(); } - private Collection filterInternalPlugins(final Collection plugins) { - return plugins - .stream() - .filter(plugin -> !(plugin instanceof DummyPluginContainer)) - .collect(Collectors.toList()); - } - @Override public void establishTags(final ResourceManager resourceManager) { final RegisterTagEventImpl event = new RegisterTagEventImpl(Cause.of(EventContext.empty(), this.game), this.game); diff --git a/src/main/java/org/spongepowered/common/SpongeMinecraftVersion.java b/src/main/java/org/spongepowered/common/SpongeMinecraftVersion.java index af6d2454179..7779480214c 100644 --- a/src/main/java/org/spongepowered/common/SpongeMinecraftVersion.java +++ b/src/main/java/org/spongepowered/common/SpongeMinecraftVersion.java @@ -70,7 +70,7 @@ public boolean isLegacy() { @Override public OptionalInt dataVersion() { - return OptionalInt.of(SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + return OptionalInt.of(SharedConstants.getCurrentVersion().dataVersion().version()); } @Override diff --git a/src/main/java/org/spongepowered/common/advancement/SpongeAdvancementBuilder.java b/src/main/java/org/spongepowered/common/advancement/SpongeAdvancementBuilder.java index 79628a11ccd..9cbc1bfc97f 100644 --- a/src/main/java/org/spongepowered/common/advancement/SpongeAdvancementBuilder.java +++ b/src/main/java/org/spongepowered/common/advancement/SpongeAdvancementBuilder.java @@ -27,20 +27,18 @@ import net.minecraft.advancements.AdvancementRequirements; import net.minecraft.advancements.AdvancementRewards; -import net.minecraft.advancements.Criterion; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.advancements.triggers.Criterion; +import net.minecraft.core.ClientAsset; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.advancement.AdvancementTemplate; +import org.spongepowered.api.advancement.Advancement; import org.spongepowered.api.advancement.DisplayInfo; import org.spongepowered.api.advancement.criteria.AdvancementCriterion; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.util.Tuple; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.advancements.AdvancementBridge; import org.spongepowered.common.item.util.ItemStackUtil; -import org.spongepowered.common.util.AbstractResourceKeyedBuilder; import org.spongepowered.common.util.SpongeCriterionUtil; import java.util.List; @@ -48,81 +46,86 @@ import java.util.Objects; import java.util.Optional; -public final class SpongeAdvancementBuilder extends AbstractResourceKeyedBuilder implements AdvancementTemplate.Builder.RootStep { +public final class SpongeAdvancementBuilder implements Advancement.Builder.RootStep { - private @Nullable ResourceLocation parent; + private @Nullable Identifier parent; private AdvancementCriterion criterion; private @Nullable DisplayInfo displayInfo; - private @Nullable ResourceLocation backgroundPath; - private DataPack pack = DataPacks.ADVANCEMENT; + private @Nullable ClientAsset backgroundPath; public SpongeAdvancementBuilder() { this.reset(); } @Override - public AdvancementTemplate.Builder parent(@Nullable AdvancementTemplate parent) { - return this.parent(parent.key()); - } - - @Override - public AdvancementTemplate.Builder parent(@Nullable ResourceKey parent) { - this.parent = (ResourceLocation) (Object) parent; + public Advancement.Builder parent(final ResourceKey parent) { + this.parent = (Identifier) (Object) parent; this.backgroundPath = null; return this; } @Override - public AdvancementTemplate.Builder.RootStep root() { + public Advancement.Builder.RootStep root() { this.parent = null; return this; } @Override - public AdvancementTemplate.Builder background(ResourceKey backgroundPath) { - this.backgroundPath = (ResourceLocation) (Object) backgroundPath; + public Advancement.Builder background(final ResourceKey backgroundPath) { + this.backgroundPath = new ClientAsset.ResourceTexture((Identifier) (Object) backgroundPath); return this; } @Override - public AdvancementTemplate.Builder criterion(AdvancementCriterion criterion) { + public Advancement.Builder criterion(final AdvancementCriterion criterion) { Objects.requireNonNull(criterion, "criterion"); this.criterion = criterion; return this; } @Override - public AdvancementTemplate.Builder displayInfo(@Nullable DisplayInfo displayInfo) { + public Advancement.Builder displayInfo(final @Nullable DisplayInfo displayInfo) { this.displayInfo = displayInfo; return this; } @Override - public AdvancementTemplate.Builder reset() { + public Advancement.Builder from(final Advancement value) { + this.parent = (Identifier) (Object) value.parent().orElse(null); + this.criterion = value.criterion(); + this.displayInfo = value.displayInfo().orElse(null); + this.backgroundPath = ((net.minecraft.advancements.Advancement) (Object) value).display() + .map(net.minecraft.advancements.DisplayInfo::getBackground).map(Optional::get).orElse(null); + return this; + } + + @Override + public Advancement.Builder reset() { this.criterion = AdvancementCriterion.empty(); this.displayInfo = null; this.parent = null; this.backgroundPath = null; - this.pack = DataPacks.ADVANCEMENT; return this; } @Override - public AdvancementTemplate build0() { + public Advancement build() { final Tuple>, List>> result = SpongeCriterionUtil.toVanillaCriteriaData(this.criterion); final AdvancementRewards rewards = AdvancementRewards.EMPTY; var displayInfo = Optional.ofNullable(this.displayInfo).map(di -> new net.minecraft.advancements.DisplayInfo( - ItemStackUtil.fromSnapshotToNative(di.icon()), + ItemStackUtil.toTemplate(di.icon()), SpongeAdventure.asVanilla(di.title()), SpongeAdventure.asVanilla(di.description()), - Optional.ofNullable(this.backgroundPath), +// TODO - Define how we want to expose ClientResource that is a combo of resource location and asset location +// Optional.ofNullable(this.backgroundPath), + Optional.empty(), (net.minecraft.advancements.AdvancementType) (Object) di.type(), di.doesShowToast(), di.doesAnnounceToChat(), di.isHidden())); final var advancement = new net.minecraft.advancements.Advancement(Optional.ofNullable((this.parent)), displayInfo, rewards, result.first(), new AdvancementRequirements(result.second()), false); ((AdvancementBridge) (Object) advancement).bridge$setCriterion(this.criterion); - return new SpongeAdvancementTemplate(this.key, advancement, this.pack); + return (Advancement) (Object) advancement; } } diff --git a/src/main/java/org/spongepowered/common/advancement/SpongeAdvancementTemplate.java b/src/main/java/org/spongepowered/common/advancement/SpongeAdvancementTemplate.java deleted file mode 100644 index 51cc0aa2914..00000000000 --- a/src/main/java/org/spongepowered/common/advancement/SpongeAdvancementTemplate.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.advancement; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.advancement.Advancement; -import org.spongepowered.api.advancement.AdvancementTemplate; -import org.spongepowered.api.advancement.AdvancementTree; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.common.SpongeCommon; - -import java.util.Optional; - -public record SpongeAdvancementTemplate(ResourceKey key, - net.minecraft.advancements.Advancement representedAdvancement, - DataPack pack - ) implements AdvancementTemplate { - - @Override - public Advancement advancement() { - return (Advancement) (Object) this.representedAdvancement; - } - - @Override - public Optional tree() { - if (this.representedAdvancement.parent().isEmpty()) { - var node = SpongeCommon.server().getAdvancements().tree().get((ResourceLocation) (Object) this.key); - return Optional.of((AdvancementTree) node); - } - return Optional.empty(); - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - return this.advancement().toContainer(); - } - - public static JsonElement encode(final net.minecraft.advancements.Advancement advancement) { - final DataResult encoded = net.minecraft.advancements.Advancement.CODEC.encodeStart(JsonOps.INSTANCE, advancement); - final JsonObject element = encoded.result().get().getAsJsonObject(); - if (element.get("rewards") != null && element.get("rewards").isJsonNull()) { - element.remove("rewards"); - } - return element; - } - - public static JsonElement encode(final AdvancementTemplate template, final RegistryAccess registryAccess) { - return SpongeAdvancementTemplate.encode((net.minecraft.advancements.Advancement) (Object) template.advancement()); - } - -} diff --git a/src/main/java/org/spongepowered/common/advancement/SpongeCriterionTrigger.java b/src/main/java/org/spongepowered/common/advancement/SpongeCriterionTrigger.java index e0274ba1d3c..e9013096422 100644 --- a/src/main/java/org/spongepowered/common/advancement/SpongeCriterionTrigger.java +++ b/src/main/java/org/spongepowered/common/advancement/SpongeCriterionTrigger.java @@ -24,15 +24,13 @@ */ package org.spongepowered.common.advancement; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; import com.google.gson.JsonObject; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.Dynamic; import com.mojang.serialization.JsonOps; import io.leangen.geantyref.TypeToken; -import net.minecraft.advancements.CriterionTrigger; +import net.minecraft.advancements.triggers.CriterionTrigger; import net.minecraft.server.PlayerAdvancements; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; @@ -51,6 +49,7 @@ import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -59,7 +58,6 @@ public class SpongeCriterionTrigger implements CriterionTrigger constructor; - private final Multimap listeners = HashMultimap.create(); final @Nullable Consumer eventHandler; private final String name; @@ -88,34 +86,26 @@ public String getName() { return this.name; } - @Override - public void addPlayerListener(final PlayerAdvancements playerAdvancementsIn, final Listener listener) { - this.listeners.put(playerAdvancementsIn, listener); - } - - @Override - public void removePlayerListener(final PlayerAdvancements playerAdvancementsIn, final Listener listener) { - this.listeners.remove(playerAdvancementsIn, listener); - } - - @Override - public void removePlayerListeners(final PlayerAdvancements playerAdvancementsIn) { - this.listeners.removeAll(playerAdvancementsIn); - } - @Override public void bridge$trigger(final ServerPlayer player) { - final PlayerAdvancements playerAdvancements = ((net.minecraft.server.level.ServerPlayer) player).getAdvancements(); + final net.minecraft.server.level.ServerPlayer mcPlayer = (net.minecraft.server.level.ServerPlayer) player; + final PlayerAdvancements playerAdvancements = mcPlayer.getAdvancements(); + final Map map = playerAdvancements.getTriggerMapForType(this); + if (map == null || map.isEmpty()) { + return; + } final Cause cause = PhaseTracker.getInstance().currentCause(); @SuppressWarnings("unchecked") // correct type verified in builder final TypeToken typeToken = (TypeToken) TypeToken.get(this.triggerConfigurationType); - for (final Listener listener : new ArrayList<>(this.listeners.get(playerAdvancements))) { - final var advancement = listener.advancement().value(); - final var advancementKey = (ResourceKey) (Object) listener.advancement().id(); - final AdvancementCriterion advancementCriterion = (AdvancementCriterion) (Object) advancement.criteria().get(listener.criterion()); + for (final var entry : new ArrayList<>(map.entrySet())) { + final PlayerAdvancements.TriggerInstanceKey key = entry.getKey(); + final SpongeFilteredTrigger triggerInstance = entry.getValue(); + final var advancement = key.advancement().value(); + final var advancementKey = (ResourceKey) (Object) key.advancement().id(); + final AdvancementCriterion advancementCriterion = (AdvancementCriterion) (Object) advancement.criteria().get(key.criterion()); final CriterionEvent.Trigger event = SpongeEventFactory.createCriterionEventTrigger(cause, (Advancement) (Object) advancement, advancementKey, advancementCriterion, - typeToken, player, (FilteredTrigger) listener.trigger(), (Trigger) advancementCriterion.type().get(), this.eventHandler == null); + typeToken, player, (FilteredTrigger) triggerInstance, (Trigger) advancementCriterion.type().get(), this.eventHandler == null); if (this.eventHandler != null) { this.eventHandler.accept(event); if (!event.result()) { @@ -124,7 +114,7 @@ public void removePlayerListeners(final PlayerAdvancements playerAdvancementsIn) } SpongeCommon.post(event); if (event.result()) { - listener.run(playerAdvancements); + playerAdvancements.award(key.advancement(), key.criterion()); } } } diff --git a/src/main/java/org/spongepowered/common/advancement/SpongeDisplayInfoBuilder.java b/src/main/java/org/spongepowered/common/advancement/SpongeDisplayInfoBuilder.java index 0f38a02050e..c83404595b6 100644 --- a/src/main/java/org/spongepowered/common/advancement/SpongeDisplayInfoBuilder.java +++ b/src/main/java/org/spongepowered/common/advancement/SpongeDisplayInfoBuilder.java @@ -26,13 +26,13 @@ import net.kyori.adventure.text.Component; -import net.minecraft.world.item.ItemStack; import org.spongepowered.api.advancement.AdvancementType; import org.spongepowered.api.advancement.AdvancementTypes; import org.spongepowered.api.advancement.DisplayInfo; import org.spongepowered.api.item.inventory.ItemStackLike; import org.spongepowered.api.item.inventory.ItemStackSnapshot; import org.spongepowered.common.adventure.SpongeAdventure; +import org.spongepowered.common.item.util.ItemStackUtil; import org.spongepowered.common.util.Preconditions; import java.util.Objects; @@ -105,7 +105,7 @@ public DisplayInfo build() { final net.minecraft.network.chat.Component title = SpongeAdventure.asVanilla(this.title); final net.minecraft.network.chat.Component description = SpongeAdventure.asVanilla(this.description); final net.minecraft.advancements.AdvancementType frameType = (net.minecraft.advancements.AdvancementType) (Object) this.advancementType; - final ItemStack icon = (ItemStack) (Object) this.icon.asMutable(); + final var icon = ItemStackUtil.toTemplate(this.icon); return (DisplayInfo) new net.minecraft.advancements.DisplayInfo(icon, title, description, null, frameType, this.showToast, this.announceToChat, this.hidden); } diff --git a/src/main/java/org/spongepowered/common/advancement/SpongeFilteredTrigger.java b/src/main/java/org/spongepowered/common/advancement/SpongeFilteredTrigger.java index 0a695d0e35d..cf0f5bf883e 100644 --- a/src/main/java/org/spongepowered/common/advancement/SpongeFilteredTrigger.java +++ b/src/main/java/org/spongepowered/common/advancement/SpongeFilteredTrigger.java @@ -27,7 +27,7 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import net.minecraft.advancements.CriterionTriggerInstance; -import net.minecraft.advancements.critereon.CriterionValidator; +import net.minecraft.world.level.storage.loot.ValidationContextSource; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.spongepowered.api.advancement.criteria.trigger.FilteredTrigger; @@ -70,8 +70,7 @@ public JsonObject serializeToJson() { } @Override - public void validate(final CriterionValidator var1) { + public void validate(ValidationContextSource validator) { } - } diff --git a/src/main/java/org/spongepowered/common/advancement/criterion/SpongeCriterionBuilder.java b/src/main/java/org/spongepowered/common/advancement/criterion/SpongeCriterionBuilder.java index 828ff5367e3..0c896960f8b 100644 --- a/src/main/java/org/spongepowered/common/advancement/criterion/SpongeCriterionBuilder.java +++ b/src/main/java/org/spongepowered/common/advancement/criterion/SpongeCriterionBuilder.java @@ -24,9 +24,9 @@ */ package org.spongepowered.common.advancement.criterion; -import net.minecraft.advancements.Criterion; -import net.minecraft.advancements.CriterionTrigger; import net.minecraft.advancements.CriterionTriggerInstance; +import net.minecraft.advancements.triggers.Criterion; +import net.minecraft.advancements.triggers.CriterionTrigger; import org.spongepowered.api.advancement.criteria.AdvancementCriterion; import org.spongepowered.common.bridge.advancements.CriterionBridge; diff --git a/src/main/java/org/spongepowered/common/advancement/criterion/SpongeDummyTrigger.java b/src/main/java/org/spongepowered/common/advancement/criterion/SpongeDummyTrigger.java index e9c244445b1..1fef191cd35 100644 --- a/src/main/java/org/spongepowered/common/advancement/criterion/SpongeDummyTrigger.java +++ b/src/main/java/org/spongepowered/common/advancement/criterion/SpongeDummyTrigger.java @@ -26,9 +26,9 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.advancements.critereon.ContextAwarePredicate; -import net.minecraft.advancements.critereon.EntityPredicate; -import net.minecraft.advancements.critereon.SimpleCriterionTrigger; +import net.minecraft.advancements.predicates.ContextAwarePredicate; +import net.minecraft.advancements.predicates.entity.EntityPredicate; +import net.minecraft.advancements.triggers.SimpleCriterionTrigger; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; diff --git a/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreCriterion.java b/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreCriterion.java index 07a662214e8..9e01ff1a742 100644 --- a/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreCriterion.java +++ b/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreCriterion.java @@ -24,9 +24,9 @@ */ package org.spongepowered.common.advancement.criterion; -import net.minecraft.advancements.Criterion; -import net.minecraft.advancements.CriterionTrigger; import net.minecraft.advancements.CriterionTriggerInstance; +import net.minecraft.advancements.triggers.Criterion; +import net.minecraft.advancements.triggers.CriterionTrigger; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.advancement.criteria.ScoreAdvancementCriterion; import org.spongepowered.api.advancement.criteria.trigger.FilteredTrigger; diff --git a/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreCriterionBuilder.java b/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreCriterionBuilder.java index e549893269a..d3da6d0c322 100644 --- a/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreCriterionBuilder.java +++ b/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreCriterionBuilder.java @@ -25,8 +25,8 @@ package org.spongepowered.common.advancement.criterion; -import net.minecraft.advancements.CriterionTrigger; import net.minecraft.advancements.CriterionTriggerInstance; +import net.minecraft.advancements.triggers.CriterionTrigger; import org.spongepowered.api.advancement.criteria.ScoreAdvancementCriterion; import org.spongepowered.common.util.Preconditions; diff --git a/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreTrigger.java b/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreTrigger.java index cf02aad5240..9463e33f4a3 100644 --- a/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreTrigger.java +++ b/src/main/java/org/spongepowered/common/advancement/criterion/SpongeScoreTrigger.java @@ -26,9 +26,9 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.advancements.critereon.ContextAwarePredicate; -import net.minecraft.advancements.critereon.EntityPredicate; -import net.minecraft.advancements.critereon.SimpleCriterionTrigger; +import net.minecraft.advancements.predicates.ContextAwarePredicate; +import net.minecraft.advancements.predicates.entity.EntityPredicate; +import net.minecraft.advancements.triggers.SimpleCriterionTrigger; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; diff --git a/src/main/java/org/spongepowered/common/adventure/AdventureTextComponent.java b/src/main/java/org/spongepowered/common/adventure/AdventureTextComponent.java index fe4f7f0e926..e6e10615035 100644 --- a/src/main/java/org/spongepowered/common/adventure/AdventureTextComponent.java +++ b/src/main/java/org/spongepowered/common/adventure/AdventureTextComponent.java @@ -24,16 +24,15 @@ */ package org.spongepowered.common.adventure; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.renderer.ComponentRenderer; -import net.minecraft.client.Minecraft; import net.minecraft.network.chat.ComponentContents; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.Style; import net.minecraft.network.chat.contents.PlainTextContents; import net.minecraft.util.FormattedCharSequence; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.bridge.adventure.ComponentBridge; @@ -48,16 +47,16 @@ public class AdventureTextComponent implements net.minecraft.network.chat.Compon private net.minecraft.network.chat.@MonotonicNonNull Component converted; private @Nullable Locale deepConvertedLocalized; private final net.kyori.adventure.text.Component wrapped; - private final @Nullable ComponentRenderer renderer; + private final @Nullable ComponentRenderer renderer; private @Nullable Locale lastLocale; private @Nullable AdventureTextComponent lastRendered; - public AdventureTextComponent(final net.kyori.adventure.text.Component wrapped, final @Nullable ComponentRenderer renderer) { + public AdventureTextComponent(final net.kyori.adventure.text.Component wrapped, final @Nullable ComponentRenderer renderer) { this.wrapped = wrapped; this.renderer = renderer; } - public @Nullable ComponentRenderer renderer() { + public @Nullable ComponentRenderer renderer() { return this.renderer; } @@ -65,12 +64,20 @@ public net.kyori.adventure.text.Component wrapped() { return this.wrapped; } - public synchronized AdventureTextComponent rendered(final Locale locale) { + public synchronized AdventureTextComponent rendered(final Locale locale, final @Nullable Audience audience) { + if (this.renderer == null) { + return this; + } + final SpongeRendererContext context = new SpongeRendererContext(locale, audience); + final Component component = this.renderer.render(this.wrapped, context); + if (context.containsVirtualComponents()) { + return new AdventureTextComponent(component, null); + } if (Objects.equals(locale, this.lastLocale)) { return this.lastRendered; } this.lastLocale = locale; - return this.lastRendered = this.renderer == null ? this : new AdventureTextComponent(this.renderer.render(this.wrapped, locale), null); + return this.lastRendered = new AdventureTextComponent(component, null); } net.minecraft.network.chat.Component deepConverted() { @@ -82,17 +89,29 @@ net.minecraft.network.chat.Component deepConverted() { return converted; } - @OnlyIn(Dist.CLIENT) net.minecraft.network.chat.Component deepConvertedLocalized() { + // Client-only: resolve locale from Minecraft settings. + // On the server, fall back to non-localized conversion. + final Locale target = AdventureTextComponent.clientLocale(); + if (target == null) { + return this.deepConverted(); + } net.minecraft.network.chat.Component converted = this.converted; - final Locale target = LocaleCache.getLocale(Minecraft.getInstance().options.languageCode); if (converted == null || this.deepConvertedLocalized != target) { - converted = this.converted = this.rendered(target).deepConverted(); + converted = this.converted = this.rendered(target, null).deepConverted(); this.deepConvertedLocalized = target; } return converted; } + private static @Nullable Locale clientLocale() { + try { + return LocaleCache.getLocale(net.minecraft.client.Minecraft.getInstance().options.languageCode); + } catch (final NoClassDefFoundError | NullPointerException e) { + return null; + } + } + public net.minecraft.network.chat.@Nullable Component deepConvertedIfPresent() { return this.converted; } @@ -104,7 +123,7 @@ public Style getStyle() { @Override public String getString() { - return this.rendered(Locale.getDefault()).deepConverted().getString(); + return this.rendered(Locale.getDefault(), null).deepConverted().getString(); } @Override @@ -136,13 +155,11 @@ public MutableComponent copy() { return this.deepConverted().copy(); } - @OnlyIn(Dist.CLIENT) @Override public FormattedCharSequence getVisualOrderText() { return this.deepConvertedLocalized().getVisualOrderText(); } - @OnlyIn(Dist.CLIENT) @Override public Optional visit(final StyledContentConsumer visitor, final Style style) { return this.deepConvertedLocalized().visit(visitor, style); diff --git a/src/main/java/org/spongepowered/common/adventure/AudienceVirtualComponentRenderer.java b/src/main/java/org/spongepowered/common/adventure/AudienceVirtualComponentRenderer.java new file mode 100644 index 00000000000..d33a05fbb5e --- /dev/null +++ b/src/main/java/org/spongepowered/common/adventure/AudienceVirtualComponentRenderer.java @@ -0,0 +1,53 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.adventure; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.VirtualComponentRenderer; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.function.Function; + +public final class AudienceVirtualComponentRenderer implements VirtualComponentRenderer { + + private final Function apply; + private final String fallbackString; + + AudienceVirtualComponentRenderer(final Function apply, final String fallbackString) { + this.apply = apply; + this.fallbackString = fallbackString; + } + + @Override + public @NonNull ComponentLike apply(final @NonNull Audience context) { + return this.apply.apply(context); + } + + @Override + public @NonNull String fallbackString() { + return this.fallbackString; + } +} diff --git a/src/main/java/org/spongepowered/common/adventure/ComponentFlattenerProvider.java b/src/main/java/org/spongepowered/common/adventure/ComponentFlattenerProvider.java index 1ba92503998..283a37c683e 100644 --- a/src/main/java/org/spongepowered/common/adventure/ComponentFlattenerProvider.java +++ b/src/main/java/org/spongepowered/common/adventure/ComponentFlattenerProvider.java @@ -25,16 +25,16 @@ package org.spongepowered.common.adventure; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.KeybindComponent; import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.TranslationArgument; import net.kyori.adventure.text.flattener.ComponentFlattener; import net.kyori.adventure.translation.GlobalTranslator; import net.kyori.adventure.translation.TranslationRegistry; import net.kyori.adventure.translation.Translator; import net.minecraft.client.KeyMapping; import net.minecraft.locale.Language; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; import org.spongepowered.common.accessor.client.KeyMappingAccessor; import org.spongepowered.common.launch.Launch; @@ -56,7 +56,7 @@ final class ComponentFlattenerProvider { builder.complexMapper(TranslatableComponent.class, (component, consumer) -> { final String key = component.key(); - for (final Translator registry : GlobalTranslator.get().sources()) { + for (final Translator registry : GlobalTranslator.translator().sources()) { if (registry instanceof TranslationRegistry && ((TranslationRegistry) registry).contains(key)) { consumer.accept(GlobalTranslator.render(component, Locale.getDefault())); return; @@ -65,7 +65,7 @@ final class ComponentFlattenerProvider { final /* @NonNull */ String translated = Language.getInstance().getOrDefault(key); final Matcher matcher = ComponentFlattenerProvider.LOCALIZATION_PATTERN.matcher(translated); - final List args = component.args(); + final List args = component.arguments(); int argPosition = 0; int lastIdx = 0; while (matcher.find()) { @@ -81,7 +81,7 @@ final class ComponentFlattenerProvider { try { final int idx = Integer.parseInt(argIdx); if (idx < args.size()) { - consumer.accept(args.get(idx)); + consumer.accept(ComponentLike.unbox(args.get(idx))); } } catch (final NumberFormatException ex) { // ignore, drop the format placeholder @@ -89,7 +89,7 @@ final class ComponentFlattenerProvider { } else { final int idx = argPosition++; if (idx < args.size()) { - consumer.accept(args.get(idx)); + consumer.accept(ComponentLike.unbox(args.get(idx))); } } } @@ -103,7 +103,7 @@ final class ComponentFlattenerProvider { INSTANCE = builder.build(); } - @OnlyIn(Dist.CLIENT) + // Client-only: guarded by !Launch.instance().dedicatedServer() in the static initializer private static String resolveKeybind(final KeybindComponent component) { final KeyMapping mapping = KeyMappingAccessor.accessor$ALL().get(component.keybind()); if (mapping != null) { diff --git a/src/main/java/org/spongepowered/common/adventure/GsonComponentSerializerProviderImpl.java b/src/main/java/org/spongepowered/common/adventure/GsonComponentSerializerProviderImpl.java index f7a144772ae..02172530d05 100644 --- a/src/main/java/org/spongepowered/common/adventure/GsonComponentSerializerProviderImpl.java +++ b/src/main/java/org/spongepowered/common/adventure/GsonComponentSerializerProviderImpl.java @@ -29,13 +29,13 @@ import java.util.function.Consumer; -@SuppressWarnings("UnstableApiUsage") // permitted provider +@SuppressWarnings({"UnstableApiUsage", "deprecation"}) // permitted provider public final class GsonComponentSerializerProviderImpl implements GsonComponentSerializer.Provider { @Override public @NonNull GsonComponentSerializer gson() { return GsonComponentSerializer.builder() - .legacyHoverEventSerializer(NbtLegacyHoverEventSerializer.INSTANCE) - .build(); + .legacyHoverEventSerializer(NbtLegacyHoverEventSerializer.INSTANCE) + .build(); } @Override diff --git a/src/main/java/org/spongepowered/common/adventure/NativeComponentRenderer.java b/src/main/java/org/spongepowered/common/adventure/NativeComponentRenderer.java index bb0d6e3abfa..e5a4b88c32e 100644 --- a/src/main/java/org/spongepowered/common/adventure/NativeComponentRenderer.java +++ b/src/main/java/org/spongepowered/common/adventure/NativeComponentRenderer.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.adventure; +import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; import net.kyori.adventure.translation.GlobalTranslator; import net.kyori.adventure.translation.TranslationRegistry; @@ -57,9 +58,9 @@ public MessageFormat translate(final @NonNull String key, final @NonNull Locale } }; - public static @NonNull Component apply(final Component input, final Locale locale) { + public static @NonNull Component apply(final Component input, final Locale locale, final @Nullable Audience audience) { if (input instanceof AdventureTextComponent) { - return ((AdventureTextComponent) input).rendered(locale); + return ((AdventureTextComponent) input).rendered(locale, audience); } else { return NativeComponentRenderer.get().render(input.copy(), locale); } @@ -78,7 +79,7 @@ public MessageFormat translate(final @NonNull String key, final @NonNull Locale /** * Gets a message format from a key and context. * - * @param key a translation key + * @param key a translation key * @param context a context * @return a message format or {@code null} to skip translation */ @@ -93,26 +94,29 @@ public Component render(@NonNull MutableComponent component, final @NonNull C co final net.minecraft.network.chat.HoverEvent hover = component.getStyle().getHoverEvent(); - if (hover != null) { + if (hover != null) { component.setStyle(component.getStyle().withHoverEvent(this.renderHoverEvent(hover, context))); } return component; } private HoverEvent renderHoverEvent(final HoverEvent input, final @NonNull C context) { - final HoverEvent.Action action = input.getAction(); - if (action == HoverEvent.Action.SHOW_TEXT) { - final Component original = input.getValue(HoverEvent.Action.SHOW_TEXT); - return new HoverEvent(HoverEvent.Action.SHOW_TEXT, this.render(original.copy(), context)); - } else if (action == HoverEvent.Action.SHOW_ENTITY) { - final HoverEvent.EntityTooltipInfo data = input.getValue(HoverEvent.Action.SHOW_ENTITY); - if (data.name.isPresent()) { - final Component rendered = this.render(data.name.get().copy(), context); - return new HoverEvent(HoverEvent.Action.SHOW_ENTITY, new HoverEvent.EntityTooltipInfo(data.type, data.id, rendered)); - } - } - - return input; + return switch (input) { + case HoverEvent.ShowText st -> { + final Component original = st.value(); + yield new HoverEvent.ShowText(this.render(original.copy(), context)); + } + case HoverEvent.ShowEntity se -> { + final HoverEvent.EntityTooltipInfo original = se.entity(); + if (original.name.isPresent()) { + final Component rendered = this.render(original.name.get().copy(), context); + final var info = new HoverEvent.EntityTooltipInfo(original.type, original.uuid, rendered); + yield new HoverEvent.ShowEntity(info); + } + yield input; + } + default -> input; + }; } protected @NonNull MutableComponent renderTranslatable(final MutableComponent component, final @NonNull TranslatableContents contents, final @NonNull C context) { @@ -135,7 +139,7 @@ private HoverEvent renderHoverEvent(final HoverEvent input, final @NonNull C con final Object[] args = contents.getArgs(); final MutableComponent result; // no arguments makes this render very simple - if(args.length == 0) { + if (args.length == 0) { result = Component.literal(format.format(null, new StringBuffer(), null).toString()); } else { result = Component.literal(""); diff --git a/src/main/java/org/spongepowered/common/adventure/NbtLegacyHoverEventSerializer.java b/src/main/java/org/spongepowered/common/adventure/NbtLegacyHoverEventSerializer.java index db6a3a6aafe..41933670532 100644 --- a/src/main/java/org/spongepowered/common/adventure/NbtLegacyHoverEventSerializer.java +++ b/src/main/java/org/spongepowered/common/adventure/NbtLegacyHoverEventSerializer.java @@ -29,7 +29,7 @@ import net.kyori.adventure.nbt.api.BinaryTagHolder; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.HoverEvent; -import net.kyori.adventure.text.serializer.gson.LegacyHoverEventSerializer; +import net.kyori.adventure.text.serializer.json.LegacyHoverEventSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.kyori.adventure.util.Codec; import net.minecraft.nbt.CompoundTag; @@ -40,10 +40,10 @@ import java.io.IOException; import java.util.UUID; -@SuppressWarnings("PatternValidation") +@SuppressWarnings({"PatternValidation","deprecation"}) public final class NbtLegacyHoverEventSerializer implements LegacyHoverEventSerializer { public static final NbtLegacyHoverEventSerializer INSTANCE = new NbtLegacyHoverEventSerializer(); - private static final Codec SNBT_CODEC = Codec.of(TagParser::parseTag, Tag::toString); + private static final Codec SNBT_CODEC = Codec.codec(TagParser::parseCompoundFully, Tag::toString); static final String ITEM_TYPE = "id"; static final String ITEM_COUNT = "Count"; @@ -61,10 +61,10 @@ public HoverEvent.ShowItem deserializeShowItem(final Component input) throws IOE final String rawContent = PlainTextComponentSerializer.plainText().serialize(input); try { final CompoundTag contents = NbtLegacyHoverEventSerializer.SNBT_CODEC.decode(rawContent); - final CompoundTag tag = contents.getCompound(NbtLegacyHoverEventSerializer.ITEM_TAG); - return HoverEvent.ShowItem.of( - Key.key(contents.getString(NbtLegacyHoverEventSerializer.ITEM_TYPE)), - contents.contains(NbtLegacyHoverEventSerializer.ITEM_COUNT) ? contents.getByte(NbtLegacyHoverEventSerializer.ITEM_COUNT) : 1, + final CompoundTag tag = contents.getCompoundOrEmpty(NbtLegacyHoverEventSerializer.ITEM_TAG); + return HoverEvent.ShowItem.showItem( + Key.key(contents.getStringOr(NbtLegacyHoverEventSerializer.ITEM_TYPE, "")), + contents.getByteOr(NbtLegacyHoverEventSerializer.ITEM_COUNT, (byte) 1), tag.isEmpty() ? null : BinaryTagHolder.encode(tag, NbtLegacyHoverEventSerializer.SNBT_CODEC) ); } catch (final CommandSyntaxException ex) { @@ -77,10 +77,10 @@ public HoverEvent.ShowItem deserializeShowItem(final Component input) throws IOE final String raw = PlainTextComponentSerializer.plainText().serialize(input); try { final CompoundTag contents = NbtLegacyHoverEventSerializer.SNBT_CODEC.decode(raw); - return HoverEvent.ShowEntity.of( - Key.key(contents.getString(NbtLegacyHoverEventSerializer.ENTITY_TYPE)), - UUID.fromString(contents.getString(NbtLegacyHoverEventSerializer.ENTITY_ID)), - componentCodec.decode(contents.getString(NbtLegacyHoverEventSerializer.ENTITY_NAME)) + return HoverEvent.ShowEntity.showEntity( + Key.key(contents.getStringOr(NbtLegacyHoverEventSerializer.ENTITY_TYPE, "")), + UUID.fromString(contents.getStringOr(NbtLegacyHoverEventSerializer.ENTITY_ID, UUID.randomUUID().toString())), + componentCodec.decode(contents.getStringOr(NbtLegacyHoverEventSerializer.ENTITY_NAME, "")) ); } catch (final CommandSyntaxException ex) { throw new IOException(ex); diff --git a/src/main/java/org/spongepowered/common/adventure/SpongeAdventure.java b/src/main/java/org/spongepowered/common/adventure/SpongeAdventure.java index ef073687f61..b3cfa95baf0 100644 --- a/src/main/java/org/spongepowered/common/adventure/SpongeAdventure.java +++ b/src/main/java/org/spongepowered/common/adventure/SpongeAdventure.java @@ -24,14 +24,15 @@ */ package org.spongepowered.common.adventure; +import com.google.gson.JsonPrimitive; +import com.mojang.authlib.GameProfile; import com.mojang.brigadier.Message; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.PrimitiveCodec; import io.netty.util.AttributeKey; import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.key.Key; import net.kyori.adventure.serializer.configurate4.ConfigurateComponentSerializer; @@ -39,71 +40,82 @@ import net.kyori.adventure.text.BlockNBTComponent; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentBuilder; +import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.EntityNBTComponent; import net.kyori.adventure.text.KeybindComponent; -import net.kyori.adventure.text.NBTComponent; import net.kyori.adventure.text.NBTComponentBuilder; +import net.kyori.adventure.text.ObjectComponent; import net.kyori.adventure.text.ScoreComponent; import net.kyori.adventure.text.SelectorComponent; import net.kyori.adventure.text.StorageNBTComponent; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.VirtualComponent; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.DataComponentValue; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.flattener.ComponentFlattener; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.ShadowColor; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.object.PlayerHeadObjectContents; +import net.kyori.adventure.text.object.SpriteObjectContents; +import net.kyori.adventure.text.renderer.ComponentRenderer; +import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.translation.GlobalTranslator; -import net.kyori.adventure.util.Codec; import net.kyori.adventure.util.TriState; import net.minecraft.ChatFormatting; -import net.minecraft.core.Holder; +import net.minecraft.commands.arguments.NbtPathArgument; +import net.minecraft.commands.arguments.coordinates.Coordinates; +import net.minecraft.commands.arguments.selector.EntitySelector; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.nbt.TagParser; import net.minecraft.network.chat.ChatType; import net.minecraft.network.chat.ComponentContents; +import net.minecraft.network.chat.FontDescription; import net.minecraft.network.chat.HoverEvent.Action; -import net.minecraft.network.chat.HoverEvent.ItemStackInfo; import net.minecraft.network.chat.MutableComponent; -import net.minecraft.network.chat.contents.BlockDataSource; -import net.minecraft.network.chat.contents.EntityDataSource; import net.minecraft.network.chat.contents.KeybindContents; import net.minecraft.network.chat.contents.NbtContents; +import net.minecraft.network.chat.contents.ObjectContents; import net.minecraft.network.chat.contents.PlainTextContents; import net.minecraft.network.chat.contents.ScoreContents; import net.minecraft.network.chat.contents.SelectorContents; -import net.minecraft.network.chat.contents.StorageDataSource; import net.minecraft.network.chat.contents.TranslatableContents; +import net.minecraft.network.chat.contents.data.BlockDataSource; +import net.minecraft.network.chat.contents.data.EntityDataSource; +import net.minecraft.network.chat.contents.data.StorageDataSource; +import net.minecraft.network.chat.contents.objects.AtlasSprite; +import net.minecraft.network.chat.contents.objects.ObjectInfo; +import net.minecraft.network.chat.contents.objects.PlayerSprite; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerBossEvent; import net.minecraft.sounds.SoundSource; +import net.minecraft.util.CompilableString; import net.minecraft.world.BossEvent; import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; +import net.minecraft.world.item.component.ResolvableProfile; +import net.minecraft.world.scores.TeamColor; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import org.spongepowered.api.adventure.ResolveOperation; import org.spongepowered.api.adventure.SpongeComponents; import org.spongepowered.api.command.CommandCause; -import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.registry.DefaultedRegistryReference; import org.spongepowered.api.util.Tristate; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.accessor.network.chat.HoverEvent_ItemStackInfoAccessor; import org.spongepowered.common.accessor.network.chat.StyleAccessor; import org.spongepowered.common.bridge.adventure.BossBarBridge; import org.spongepowered.common.bridge.adventure.ComponentBridge; @@ -111,10 +123,12 @@ import org.spongepowered.common.bridge.world.BossEventBridge; import org.spongepowered.common.launch.Launch; -import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -126,25 +140,11 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; +import java.util.function.Function; public final class SpongeAdventure { public static final AttributeKey CHANNEL_LOCALE = AttributeKey.newInstance("sponge:locale"); - public static final Codec NBT_CODEC = new Codec() { - @Override - public @NonNull CompoundTag decode(final @NonNull String encoded) throws IOException { - try { - return TagParser.parseTag(encoded); - } catch (final CommandSyntaxException e) { - throw new IOException(e); - } - } - - @Override - public @NonNull String encode(final @NonNull CompoundTag decoded) { - return decoded.toString(); - } - }; public static final PrimitiveCodec STRING_CODEC = new PrimitiveCodec() { @Override @@ -163,13 +163,59 @@ public String toString() { } }; + public static final ComponentRenderer SPONGE_VIRTUAL_COMPONENT_RENDERER = new ComponentRenderer() { + + @Override + public @NotNull Component render(final @NotNull Component component, final @NotNull SpongeRendererContext context) { + if (component instanceof final VirtualComponent virtualComponent) { + return this.renderVirtual(virtualComponent, context); + } + return component; + } + + public @NonNull Component renderVirtual(final @NonNull VirtualComponent component, final @NonNull SpongeRendererContext context) { + context.markContainsVirtualComponents(); + if (component.renderer() instanceof final AudienceVirtualComponentRenderer audienceVirtualComponentRenderer + && context.audience() != null) { + return audienceVirtualComponentRenderer.apply(context.audience()).asComponent(); + } + return component; + } + }; + + public static final ComponentRenderer SPONGE_COMPONENT_RENDERER = new TranslatableComponentRenderer<>() { + + @Override + protected @NonNull Component renderVirtual(final @NonNull VirtualComponent component, final @NonNull SpongeRendererContext context) { + return SpongeAdventure.SPONGE_VIRTUAL_COMPONENT_RENDERER.render(component, context); + } + + @Override + protected @Nullable MessageFormat translate(final @NonNull String key, final @NonNull SpongeRendererContext context) { + return GlobalTranslator.translator().translate(key, context.locale()); + } + + @Override + protected @NonNull Component renderTranslatable(final @NonNull TranslatableComponent component, final @NonNull SpongeRendererContext context) { + final TriState anyTranslations = GlobalTranslator.translator().hasAnyTranslations(); + if (anyTranslations == TriState.TRUE || anyTranslations == TriState.NOT_SET) { + final @Nullable Component translated = GlobalTranslator.translator().translate(component, context.locale()); + return translated != null + ? translated + : super.renderTranslatable(component, context); + } + return component; + } + }; + public static final ConfigurateComponentSerializer CONFIGURATE = ConfigurateComponentSerializer.builder() - .scalarSerializer(GsonComponentSerializer.gson()) - .build(); + .scalarSerializer(GsonComponentSerializer.gson()) + .build(); private static final Set ACTIVE_BOSS_BARS = ConcurrentHashMap.newKeySet(); public static final ThreadLocal ENCODING_LOCALE = new ThreadLocal<>(); + public static final ThreadLocal ENCODING_PLAYER = new ThreadLocal<>(); // -------------- // ---- Core ---- @@ -191,10 +237,19 @@ public static TriState asAdventure(final Tristate state) { // ------------- // org.spongepowered.common.mixin.core.adventure.KeyMixin - public static Key asAdventure(final ResourceLocation key) { + public static Key asAdventure(final Identifier key) { return (Key) (Object) key; } + public static Key asAdventure(final FontDescription description) { + return switch (description) { + case null -> null; + case FontDescription.Resource fr -> Key.key(fr.id().toString()); + case FontDescription.AtlasSprite as -> Key.key(as.spriteId().toString()); + default -> throw new IllegalStateException("Unexpected value: " + description); + }; + } + // ------------------------ // ---- ChatType.Bound ---- // ------------------------ @@ -229,13 +284,25 @@ public static ChatType.Bound asVanilla(final RegistryAccess access, final net.ky } public static net.minecraft.network.chat.Component asVanilla(final Component component) { - return new AdventureTextComponent(component, GlobalTranslator.renderer()); + return new AdventureTextComponent(component, SpongeAdventure.SPONGE_COMPONENT_RENDERER); } public static Optional asVanillaOpt(final @Nullable Component component) { return component == null ? Optional.empty() : Optional.of(((ComponentBridge) component).bridge$asVanillaComponent()); } + private static CompilableString parseSelector(final String pattern) { + return EntitySelector.COMPILABLE_CODEC.parse(JsonOps.INSTANCE, new JsonPrimitive(pattern)).getOrThrow(); + } + + private static CompilableString parseNbtPath(final String path) { + return NbtContents.NBT_PATH_CODEC.parse(JsonOps.INSTANCE, new JsonPrimitive(path)).getOrThrow(); + } + + private static CompilableString parseBlockPos(final String pos) { + return BlockDataSource.BLOCK_POS_CODEC.parse(JsonOps.INSTANCE, new JsonPrimitive(pos)).getOrThrow(); + } + // no caching public static MutableComponent asVanillaMutable(final Component component) { final MutableComponent vanilla = SpongeAdventure.asVanillaMutable0(component); @@ -246,44 +313,49 @@ public static MutableComponent asVanillaMutable(final Component component) { return vanilla; } + @SuppressWarnings("deprecation") private static MutableComponent asVanillaMutable0(final Component component) { - if (component instanceof TextComponent) { - return net.minecraft.network.chat.Component.literal(((TextComponent) component).content()); - } - if (component instanceof final TranslatableComponent $this) { - final List with = new ArrayList<>($this.args().size()); - for (final Component arg : $this.args()) { - with.add(((ComponentBridge) arg).bridge$asVanillaComponent()); - } - return net.minecraft.network.chat.Component.translatable($this.key(), with.toArray(new Object[0])); - } - if (component instanceof KeybindComponent) { - return net.minecraft.network.chat.Component.keybind(((KeybindComponent) component).keybind()); - } - if (component instanceof final ScoreComponent $this) { - return net.minecraft.network.chat.Component.score($this.name(), $this.objective()); - } - if (component instanceof final SelectorComponent $this) { - return net.minecraft.network.chat.Component.selector($this.pattern(), SpongeAdventure.asVanillaOpt($this.separator())); - } - if (component instanceof NBTComponent) { - if (component instanceof final BlockNBTComponent $this) { - return net.minecraft.network.chat.Component.nbt($this.nbtPath(), $this.interpret(), - SpongeAdventure.asVanillaOpt($this.separator()), - new BlockDataSource($this.pos().asString())); - } - if (component instanceof final EntityNBTComponent $this) { - return net.minecraft.network.chat.Component.nbt($this.nbtPath(), $this.interpret(), - SpongeAdventure.asVanillaOpt($this.separator()), - new EntityDataSource($this.selector())); + return switch (component) { + case VirtualComponent virtual -> net.minecraft.network.chat.Component.literal(virtual.content()); + case TextComponent text -> net.minecraft.network.chat.Component.literal(text.content()); + case TranslatableComponent translatable -> { + final List with = new ArrayList<>(translatable.arguments().size()); + for (final Component arg : ComponentLike.asComponents(translatable.arguments())) { + with.add(((ComponentBridge) arg).bridge$asVanillaComponent()); + } + yield net.minecraft.network.chat.Component.translatable(translatable.key(), with.toArray(new Object[0])); } - if (component instanceof final StorageNBTComponent $this) { - return net.minecraft.network.chat.Component.nbt($this.nbtPath(), $this.interpret(), - SpongeAdventure.asVanillaOpt($this.separator()), - new StorageDataSource(SpongeAdventure.asVanilla($this.storage()))); - } - } - throw new UnsupportedOperationException("Cannot convert Component of type " + component.getClass()); + case KeybindComponent keybind -> net.minecraft.network.chat.Component.keybind(keybind.keybind()); + case ScoreComponent score -> net.minecraft.network.chat.Component.score(score.name(), score.objective()); + case SelectorComponent selector -> net.minecraft.network.chat.Component.selector( + SpongeAdventure.parseSelector(selector.pattern()), SpongeAdventure.asVanillaOpt(selector.separator()) + ); + case BlockNBTComponent block -> net.minecraft.network.chat.Component.nbt( + SpongeAdventure.parseNbtPath(block.nbtPath()), block.interpret(), false, + SpongeAdventure.asVanillaOpt(block.separator()), + new BlockDataSource(SpongeAdventure.parseBlockPos(block.pos().asString()))); + case EntityNBTComponent entity -> + net.minecraft.network.chat.Component.nbt( + SpongeAdventure.parseNbtPath(entity.nbtPath()), entity.interpret(), false, + SpongeAdventure.asVanillaOpt(entity.separator()), + new EntityDataSource(SpongeAdventure.parseSelector(entity.selector()))); + case StorageNBTComponent storage -> + net.minecraft.network.chat.Component.nbt( + SpongeAdventure.parseNbtPath(storage.nbtPath()), storage.interpret(), false, + SpongeAdventure.asVanillaOpt(storage.separator()), + new StorageDataSource(SpongeAdventure.asVanilla(storage.storage()))); + case ObjectComponent obj -> net.minecraft.network.chat.Component.object(SpongeAdventure.asVanilla(obj.contents())); + default -> + throw new UnsupportedOperationException("Cannot convert Component of type " + component.getClass()); + }; + } + + private static ObjectInfo asVanilla(final net.kyori.adventure.text.object.ObjectContents obj) { + return switch (obj) { + case SpriteObjectContents sprite -> new AtlasSprite(SpongeAdventure.asVanilla(sprite.atlas()), SpongeAdventure.asVanilla(sprite.sprite())); + case PlayerHeadObjectContents player -> new PlayerSprite(player.id() == null ? ResolvableProfile.createUnresolved(player.name()) : ResolvableProfile.createUnresolved(player.id()), player.hat()); + default -> throw new UnsupportedOperationException("Cannot convert ObjectContents of type " + obj.getClass()); + }; } // no caching @@ -298,51 +370,60 @@ public static Component asAdventure(final net.minecraft.network.chat.Component c builder.append(SpongeAdventure.asAdventure(child)); } - builder.style(((org.spongepowered.common.bridge.network.chat.StyleBridge) component.getStyle()).bridge$asAdventure()); + builder.style(((org.spongepowered.common.bridge.network.chat.StyleBridge) (Object) component.getStyle()).bridge$asAdventure()); return builder.build(); } + @SuppressWarnings("deprecation") private static ComponentBuilder asAdventureBuilder(final ComponentContents contents) { - if (contents instanceof final PlainTextContents lc) { - if (contents == PlainTextContents.EMPTY) { - return Component.empty().toBuilder(); + return switch (contents) { + case PlainTextContents plain -> { + if (plain == PlainTextContents.EMPTY) { + yield Component.empty().toBuilder(); + } + yield Component.text().content(plain.text()); } - return Component.text().content(lc.text()); - } - if (contents instanceof final TranslatableContents tc) { - final List argList = Arrays.stream(tc.getArgs()) + case TranslatableContents tc -> { + final List argList = Arrays.stream(tc.getArgs()) .map(arg -> arg instanceof final net.minecraft.network.chat.Component argComponent ? - SpongeAdventure.asAdventure(argComponent) : Component.text(arg.toString())).toList(); - return Component.translatable().key(tc.getKey()).args(argList); - } - if (contents instanceof final KeybindContents kc) { - return Component.keybind().keybind(kc.getName()); - } - if (contents instanceof final ScoreContents sc) { - return Component.score().name(sc.getName()).objective(sc.getObjective()); - } - if (contents instanceof final SelectorContents sc) { - return Component.selector().pattern(sc.getPattern()) - .separator(SpongeAdventure.asAdventure(sc.getSeparator())); - } - if (contents instanceof final NbtContents nc) { - NBTComponentBuilder nbtBuilder; - if (nc.getDataSource() instanceof final BlockDataSource ds) { - nbtBuilder = Component.blockNBT().pos(BlockNBTComponent.Pos.fromString(ds.posPattern())); - } else if (nc.getDataSource() instanceof final EntityDataSource ds) { - nbtBuilder = Component.entityNBT().selector(ds.selectorPattern()); - } else if (nc.getDataSource() instanceof final StorageDataSource ds) { - nbtBuilder = Component.storageNBT().storage(SpongeAdventure.asAdventure(ds.id())); - } else { - throw new UnsupportedOperationException("Cannot convert NBTContents with DataSource " + nc.getDataSource().getClass()); + SpongeAdventure.asAdventure(argComponent) : Component.text(arg.toString())).toList(); + yield Component.translatable().key(tc.getKey()).arguments(argList); } - return nbtBuilder.nbtPath(nc.getNbtPath()) - .interpret(nc.isInterpreting()) - .separator(SpongeAdventure.asAdventure(nc.getSeparator())); - } + case KeybindContents kc -> Component.keybind().keybind(kc.getName()); + case ScoreContents sc -> + Component.score().name(sc.name().mapLeft(CompilableString::source).orThrow()).objective(sc.objective()); + case SelectorContents sc -> + Component.selector().pattern(sc.selector().source()).separator(SpongeAdventure.asAdventure(sc.separator())); + case NbtContents nbt -> { + final NBTComponentBuilder nbtBuilder = switch (nbt.dataSource()) { + case BlockDataSource bds -> + Component.blockNBT().pos(BlockNBTComponent.Pos.fromString(bds.coordinates().source())); + case EntityDataSource eds -> Component.entityNBT().selector(eds.selector().source()); + case StorageDataSource sds -> Component.storageNBT().storage(SpongeAdventure.asAdventure(sds.id())); + default -> + throw new UnsupportedOperationException("Cannot convert NBTContents with DataSource " + nbt.dataSource().getClass()); + }; + yield nbtBuilder.nbtPath(nbt.nbtPath().source()) + .interpret(nbt.interpreting()) + .separator(SpongeAdventure.asAdventure(nbt.separator())); + } + case ObjectContents obj -> Component.object().contents(SpongeAdventure.asAdventure(obj.contents())); + default -> + throw new UnsupportedOperationException("Cannot convert ComponentContents of type " + contents.getClass()); + }; + } - throw new UnsupportedOperationException("Cannot convert ComponentContents of type " + contents.getClass()); + private static net.kyori.adventure.text.object.ObjectContents asAdventure(final ObjectInfo info) { + switch (info) { + case AtlasSprite atlas: + return net.kyori.adventure.text.object.ObjectContents.sprite(SpongeAdventure.asAdventure(atlas.atlas()), SpongeAdventure.asAdventure(atlas.sprite())); + case PlayerSprite player: + final GameProfile profile = player.player().partialProfile(); + return net.kyori.adventure.text.object.ObjectContents.playerHead().name(profile.name()).id(profile.id()).hat(player.hat()).build(); + default: + throw new UnsupportedOperationException("Cannot convert ObjectInfo of type " + info.getClass()); + } } public static @Nullable Component asAdventure(final Optional component) { @@ -351,8 +432,8 @@ public static Component asAdventure(final net.minecraft.network.chat.Component c // no caching public static Style asAdventure(final net.minecraft.network.chat.Style mcStyle) { - final net.kyori.adventure.text.format.Style.Builder builder = net.kyori.adventure.text.format.Style.style(); - final StyleAccessor $access = (StyleAccessor) mcStyle; + final Style.Builder builder = Style.style(); + final StyleAccessor $access = (StyleAccessor) (Object) mcStyle; builder.font(SpongeAdventure.asAdventure($access.accessor$font())); // font builder.color(SpongeAdventure.asAdventure(mcStyle.getColor())); // color @@ -369,7 +450,7 @@ public static Style asAdventure(final net.minecraft.network.chat.Style mcStyle) } final net.minecraft.network.chat.ClickEvent clickEvent = mcStyle.getClickEvent(); if (clickEvent != null) { - builder.clickEvent(ClickEvent.clickEvent(SpongeAdventure.asAdventure(clickEvent.getAction()), clickEvent.getValue())); + builder.clickEvent(SpongeAdventure.asAdventure(clickEvent)); } builder.insertion(mcStyle.getInsertion()); // insertion return builder.build(); @@ -439,53 +520,128 @@ public static ChatFormatting asVanilla(final NamedTextColor color) { return TextColor.color(color.getValue()); } - public static @Nullable TextColor asAdventure(final ChatFormatting formatting) { + public static @Nullable TeamColor asVanillaTeamColor(final @Nullable ChatFormatting formatting) { if (formatting == null) { return null; } - final Integer color = formatting.getColor(); + return switch (formatting) { + case BLACK -> TeamColor.BLACK; + case DARK_BLUE -> TeamColor.DARK_BLUE; + case DARK_GREEN -> TeamColor.DARK_GREEN; + case DARK_AQUA -> TeamColor.DARK_AQUA; + case DARK_RED -> TeamColor.DARK_RED; + case DARK_PURPLE -> TeamColor.DARK_PURPLE; + case GOLD -> TeamColor.GOLD; + case GRAY -> TeamColor.GRAY; + case DARK_GRAY -> TeamColor.DARK_GRAY; + case BLUE -> TeamColor.BLUE; + case GREEN -> TeamColor.GREEN; + case AQUA -> TeamColor.AQUA; + case RED -> TeamColor.RED; + case LIGHT_PURPLE -> TeamColor.LIGHT_PURPLE; + case YELLOW -> TeamColor.YELLOW; + case WHITE -> TeamColor.WHITE; + default -> null; + }; + } + + public static @Nullable TextColor asAdventure(final @Nullable ChatFormatting formatting) { + return SpongeAdventure.asAdventure(SpongeAdventure.asVanillaTeamColor(formatting)); + } + + public static @Nullable TeamColor asVanillaTeamColor(final @Nullable NamedTextColor color) { + if (color == null) { + return null; + } + if (color == NamedTextColor.BLACK) { + return TeamColor.BLACK; + } else if (color == NamedTextColor.DARK_BLUE) { + return TeamColor.DARK_BLUE; + } else if (color == NamedTextColor.DARK_GREEN) { + return TeamColor.DARK_GREEN; + } else if (color == NamedTextColor.DARK_AQUA) { + return TeamColor.DARK_AQUA; + } else if (color == NamedTextColor.DARK_RED) { + return TeamColor.DARK_RED; + } else if (color == NamedTextColor.DARK_PURPLE) { + return TeamColor.DARK_PURPLE; + } else if (color == NamedTextColor.GOLD) { + return TeamColor.GOLD; + } else if (color == NamedTextColor.GRAY) { + return TeamColor.GRAY; + } else if (color == NamedTextColor.DARK_GRAY) { + return TeamColor.DARK_GRAY; + } else if (color == NamedTextColor.BLUE) { + return TeamColor.BLUE; + } else if (color == NamedTextColor.GREEN) { + return TeamColor.GREEN; + } else if (color == NamedTextColor.AQUA) { + return TeamColor.AQUA; + } else if (color == NamedTextColor.RED) { + return TeamColor.RED; + } else if (color == NamedTextColor.LIGHT_PURPLE) { + return TeamColor.LIGHT_PURPLE; + } else if (color == NamedTextColor.YELLOW) { + return TeamColor.YELLOW; + } else if (color == NamedTextColor.WHITE) { + return TeamColor.WHITE; + } + return null; + } + + public static @Nullable TextColor asAdventure(final @Nullable TeamColor color) { + if (color == null) { + return null; + } + return TextColor.color(color.rgb()); + } + + public static @Nullable NamedTextColor asAdventureNamed(final @Nullable TeamColor color) { if (color == null) { return null; } - return TextColor.color(color); + return switch (color) { + case BLACK -> NamedTextColor.BLACK; + case DARK_BLUE -> NamedTextColor.DARK_BLUE; + case DARK_GREEN -> NamedTextColor.DARK_GREEN; + case DARK_AQUA -> NamedTextColor.DARK_AQUA; + case DARK_RED -> NamedTextColor.DARK_RED; + case DARK_PURPLE -> NamedTextColor.DARK_PURPLE; + case GOLD -> NamedTextColor.GOLD; + case GRAY -> NamedTextColor.GRAY; + case DARK_GRAY -> NamedTextColor.DARK_GRAY; + case BLUE -> NamedTextColor.BLUE; + case GREEN -> NamedTextColor.GREEN; + case AQUA -> NamedTextColor.AQUA; + case RED -> NamedTextColor.RED; + case LIGHT_PURPLE -> NamedTextColor.LIGHT_PURPLE; + case YELLOW -> NamedTextColor.YELLOW; + case WHITE -> NamedTextColor.WHITE; + }; } public static @Nullable NamedTextColor asAdventureNamed(final @Nullable ChatFormatting color) { if (color == null) { return null; } - if (color == ChatFormatting.BLACK) { - return NamedTextColor.BLACK; - } else if (color == ChatFormatting.DARK_BLUE) { - return NamedTextColor.DARK_BLUE; - } else if (color == ChatFormatting.DARK_GREEN) { - return NamedTextColor.DARK_GREEN; - } else if (color == ChatFormatting.DARK_AQUA) { - return NamedTextColor.DARK_AQUA; - } else if (color == ChatFormatting.DARK_RED) { - return NamedTextColor.DARK_RED; - } else if (color == ChatFormatting.DARK_PURPLE) { - return NamedTextColor.DARK_PURPLE; - } else if (color == ChatFormatting.GOLD) { - return NamedTextColor.GOLD; - } else if (color == ChatFormatting.GRAY) { - return NamedTextColor.GRAY; - } else if (color == ChatFormatting.DARK_GRAY) { - return NamedTextColor.DARK_GRAY; - } else if (color == ChatFormatting.BLUE) { - return NamedTextColor.BLUE; - } else if (color == ChatFormatting.GREEN) { - return NamedTextColor.GREEN; - } else if (color == ChatFormatting.AQUA) { - return NamedTextColor.AQUA; - } else if (color == ChatFormatting.RED) { - return NamedTextColor.RED; - } else if (color == ChatFormatting.LIGHT_PURPLE) { - return NamedTextColor.LIGHT_PURPLE; - } else if (color == ChatFormatting.YELLOW) { - return NamedTextColor.YELLOW; - } - return NamedTextColor.WHITE; + return switch (color) { + case BLACK -> NamedTextColor.BLACK; + case DARK_BLUE -> NamedTextColor.DARK_BLUE; + case DARK_GREEN -> NamedTextColor.DARK_GREEN; + case DARK_AQUA -> NamedTextColor.DARK_AQUA; + case DARK_RED -> NamedTextColor.DARK_RED; + case DARK_PURPLE -> NamedTextColor.DARK_PURPLE; + case GOLD -> NamedTextColor.GOLD; + case GRAY -> NamedTextColor.GRAY; + case DARK_GRAY -> NamedTextColor.DARK_GRAY; + case BLUE -> NamedTextColor.BLUE; + case GREEN -> NamedTextColor.GREEN; + case AQUA -> NamedTextColor.AQUA; + case RED -> NamedTextColor.RED; + case LIGHT_PURPLE -> NamedTextColor.LIGHT_PURPLE; + case YELLOW -> NamedTextColor.YELLOW; + default -> NamedTextColor.WHITE; // White color is also just defaulting to white + }; } public static @Nullable Boolean asVanillaNullable(final TextDecoration.State state) { @@ -499,36 +655,78 @@ public static ChatFormatting asVanilla(final NamedTextColor color) { @SuppressWarnings("ConstantConditions") public static HoverEvent asAdventure(final net.minecraft.network.chat.HoverEvent event) { - final Action action = event.getAction(); - if (action == Action.SHOW_TEXT) { - return HoverEvent.showText(SpongeAdventure.asAdventure(event.getValue(Action.SHOW_TEXT))); - } else if (action == Action.SHOW_ENTITY) { - final net.minecraft.network.chat.HoverEvent.EntityTooltipInfo value = event.getValue( - Action.SHOW_ENTITY); - final Registry> entityTypeRegistry = SpongeCommon.vanillaRegistry(Registries.ENTITY_TYPE); - return HoverEvent.showEntity( - SpongeAdventure.asAdventure(entityTypeRegistry.getKey(value.type)), - value.id, - SpongeAdventure.asAdventure(value.name) - ); - } else if (action == Action.SHOW_ITEM) { - final ItemStackInfo value = event.getValue(Action.SHOW_ITEM); - final Registry itemRegistry = SpongeCommon.vanillaRegistry(Registries.ITEM); - final ItemStack itemStack = value.getItemStack(); - return HoverEvent.showItem( - SpongeAdventure.asAdventure(itemRegistry.getKey(itemStack.getItem())), - itemStack.getCount(), - SpongeAdventure.asAdventure(itemStack.getComponentsPatch()) - ); + return switch (event) { + case net.minecraft.network.chat.HoverEvent.ShowItem si: + final var stack = si.item(); + final Registry itemRegistry = SpongeCommon.vanillaRegistry(Registries.ITEM); + yield HoverEvent.showItem( + SpongeAdventure.asAdventure(stack.typeHolder().unwrap().map(ResourceKey::identifier, itemRegistry::getKey)), + stack.count(), + SpongeAdventure.asAdventure(stack.components()) + ); + case net.minecraft.network.chat.HoverEvent.ShowEntity se: + final net.minecraft.network.chat.HoverEvent.EntityTooltipInfo value = se.entity(); + final Registry> entityTypeRegistry = SpongeCommon.vanillaRegistry(Registries.ENTITY_TYPE); + yield HoverEvent.showEntity( + SpongeAdventure.asAdventure(entityTypeRegistry.getKey(value.type)), + value.uuid, + SpongeAdventure.asAdventure(value.name) + ); + case net.minecraft.network.chat.HoverEvent.ShowText st: + yield HoverEvent.showText(SpongeAdventure.asAdventure(st.value())); + default: + throw new IllegalStateException("Unexpected value: " + event); + }; + } + + public static @Nullable ClickEvent asAdventure(final net.minecraft.network.chat.@Nullable ClickEvent ce) { + if (ce == null) { + return null; } - throw new IllegalArgumentException(event.toString()); + return switch (ce) { + case net.minecraft.network.chat.ClickEvent.OpenUrl ou -> { + try { + yield ClickEvent.openUrl(ou.uri().toURL()); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Invalid URL: " + ou.uri(), e); + } + } + case net.minecraft.network.chat.ClickEvent.OpenFile of -> ClickEvent.openFile(of.file().getPath()); + case net.minecraft.network.chat.ClickEvent.RunCommand rc -> ClickEvent.runCommand(rc.command()); + case net.minecraft.network.chat.ClickEvent.SuggestCommand sc -> ClickEvent.suggestCommand(sc.command()); + case net.minecraft.network.chat.ClickEvent.ChangePage cp -> ClickEvent.changePage(cp.page()); + default -> throw new IllegalStateException("Unexpected value: " + ce); + }; } public static net.minecraft.network.chat.@Nullable ClickEvent asVanillaNullable(final @Nullable ClickEvent event) { if (event == null) { return null; } - return new net.minecraft.network.chat.ClickEvent(SpongeAdventure.asVanilla(event.action()), event.value()); + return switch (event.action()) { + case OPEN_URL ->{ + URI page; + try { + page = new URI(event.value()); + } catch (final URISyntaxException e) { + throw new IllegalArgumentException("Invalid url: " + event.value(), e); + } + yield new net.minecraft.network.chat.ClickEvent.OpenUrl(page); + } + case COPY_TO_CLIPBOARD -> new net.minecraft.network.chat.ClickEvent.CopyToClipboard(event.value()); + case RUN_COMMAND -> new net.minecraft.network.chat.ClickEvent.RunCommand(event.value()); + case SUGGEST_COMMAND -> new net.minecraft.network.chat.ClickEvent.SuggestCommand(event.value()); + case CHANGE_PAGE ->{ + int page; + try { + page = Integer.parseInt(event.value()); + } catch (final NumberFormatException e) { + throw new IllegalArgumentException("Invalid page number: " + event.value(), e); + } + yield new net.minecraft.network.chat.ClickEvent.ChangePage(page); + } + default -> throw new IllegalArgumentException("Unknown click action: " + event.action()); + }; } public static net.minecraft.network.chat.@Nullable HoverEvent asVanillaNullable(final @Nullable HoverEvent event) { @@ -537,17 +735,13 @@ public static HoverEvent asAdventure(final net.minecraft.network.chat.HoverEv } final HoverEvent.Action action = event.action(); if (action == HoverEvent.Action.SHOW_TEXT) { - return new net.minecraft.network.chat.HoverEvent( - Action.SHOW_TEXT, - SpongeAdventure.asVanilla((Component) event.value()) - ); + return new net.minecraft.network.chat.HoverEvent.ShowText(SpongeAdventure.asVanilla((Component) event.value())); } else if (action == HoverEvent.Action.SHOW_ENTITY) { final HoverEvent.ShowEntity value = (HoverEvent.ShowEntity) event.value(); final Registry> entityTypeRegistry = SpongeCommon.vanillaRegistry(Registries.ENTITY_TYPE); - return new net.minecraft.network.chat.HoverEvent( - Action.SHOW_ENTITY, + return new net.minecraft.network.chat.HoverEvent.ShowEntity( new net.minecraft.network.chat.HoverEvent.EntityTooltipInfo( - entityTypeRegistry.get(SpongeAdventure.asVanilla(value.type())), + entityTypeRegistry.getValue(SpongeAdventure.asVanilla(value.type())), value.id(), SpongeAdventure.asVanillaNullable(value.name()) ) @@ -555,19 +749,13 @@ public static HoverEvent asAdventure(final net.minecraft.network.chat.HoverEv } else if (action == HoverEvent.Action.SHOW_ITEM) { final HoverEvent.ShowItem value = (HoverEvent.ShowItem) event.value(); final Registry itemRegistry = SpongeCommon.vanillaRegistry(Registries.ITEM); - return new net.minecraft.network.chat.HoverEvent( - Action.SHOW_ITEM, - HoverEvent_ItemStackInfoAccessor.invoker$new( - Holder.direct(itemRegistry.get(SpongeAdventure.asVanilla(value.item()))), - value.count(), - SpongeAdventure.asVanilla(value.dataComponents()) - ) - ); + final var item = itemRegistry.wrapAsHolder(itemRegistry.getValue(SpongeAdventure.asVanilla(value.item()))); + return new net.minecraft.network.chat.HoverEvent.ShowItem(new ItemStackTemplate(item, value.count(), SpongeAdventure.asVanilla(value.dataComponents()))); } throw new IllegalArgumentException(event.toString()); } - public static Action asVanilla(final HoverEvent.Action action) { + public static Action asVanilla(final HoverEvent.Action action) { if (action == HoverEvent.Action.SHOW_TEXT) { return Action.SHOW_TEXT; } else if (action == HoverEvent.Action.SHOW_ITEM) { @@ -621,15 +809,6 @@ public static List json(final List strings) { return components; } - public static ListTag listTagJson(final List components) { - final GsonComponentSerializer gcs = GsonComponentSerializer.gson(); - final ListTag nbt = new ListTag(); - for (final Component component : components) { - nbt.add(StringTag.valueOf(gcs.serialize(component))); - } - return nbt; - } - // ----------------- // ---- BossBar ---- // ----------------- @@ -643,38 +822,38 @@ public static ServerBossEvent asVanillaServer(final BossBar bar) { } public static BossEvent.BossBarColor asVanilla(final BossBar.Color color) { - if(color == BossBar.Color.PINK) { + if (color == BossBar.Color.PINK) { return BossEvent.BossBarColor.PINK; - } else if(color == BossBar.Color.BLUE) { + } else if (color == BossBar.Color.BLUE) { return BossEvent.BossBarColor.BLUE; - } else if(color == BossBar.Color.RED) { + } else if (color == BossBar.Color.RED) { return BossEvent.BossBarColor.RED; - } else if(color == BossBar.Color.GREEN) { + } else if (color == BossBar.Color.GREEN) { return BossEvent.BossBarColor.GREEN; - } else if(color == BossBar.Color.YELLOW) { + } else if (color == BossBar.Color.YELLOW) { return BossEvent.BossBarColor.YELLOW; - } else if(color == BossBar.Color.PURPLE) { + } else if (color == BossBar.Color.PURPLE) { return BossEvent.BossBarColor.PURPLE; - } else if(color == BossBar.Color.WHITE) { + } else if (color == BossBar.Color.WHITE) { return BossEvent.BossBarColor.WHITE; } throw new IllegalArgumentException(color.name()); } public static BossBar.Color asAdventure(final BossEvent.BossBarColor color) { - if(color == BossEvent.BossBarColor.PINK) { + if (color == BossEvent.BossBarColor.PINK) { return BossBar.Color.PINK; - } else if(color == BossEvent.BossBarColor.BLUE) { + } else if (color == BossEvent.BossBarColor.BLUE) { return BossBar.Color.BLUE; - } else if(color == BossEvent.BossBarColor.RED) { + } else if (color == BossEvent.BossBarColor.RED) { return BossBar.Color.RED; - } else if(color == BossEvent.BossBarColor.GREEN) { + } else if (color == BossEvent.BossBarColor.GREEN) { return BossBar.Color.GREEN; - } else if(color == BossEvent.BossBarColor.YELLOW) { + } else if (color == BossEvent.BossBarColor.YELLOW) { return BossBar.Color.YELLOW; - } else if(color == BossEvent.BossBarColor.PURPLE) { + } else if (color == BossEvent.BossBarColor.PURPLE) { return BossBar.Color.PURPLE; - } else if(color == BossEvent.BossBarColor.WHITE) { + } else if (color == BossEvent.BossBarColor.WHITE) { return BossBar.Color.WHITE; } throw new IllegalArgumentException(color.name()); @@ -724,18 +903,17 @@ public static Set asAdventureFlags(final boolean darkenScreen, fin return flags; } - @SuppressWarnings({"rawtypes", "unchecked"}) + @SuppressWarnings({"rawtypes", "unchecked" }) public static DataComponentPatch asVanilla(final Map componentMap) { if (componentMap == null) { return DataComponentPatch.EMPTY; } final DataComponentPatch.Builder builder = DataComponentPatch.builder(); - componentMap.forEach((key, value) -> { - final DataComponentType type = BuiltInRegistries.DATA_COMPONENT_TYPE.get(SpongeAdventure.asVanilla(key)); - if (type != null && value instanceof SpongeDataComponentValue dcv) { - builder.set(type, dcv.value.orElse(null)); + componentMap.forEach((key, value) -> BuiltInRegistries.DATA_COMPONENT_TYPE.getOptional(SpongeAdventure.asVanilla(key)).ifPresent(type -> { + if (value instanceof SpongeDataComponentValue(Optional value1)) { + builder.set((DataComponentType) type, value1.orElse(null)); } - }); + })); return builder.build(); } @@ -746,64 +924,79 @@ public static Map asAdventure(final DataComponentPatch Map map = new HashMap<>(); components.entrySet().forEach(entry -> { - final ResourceLocation key = BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(entry.getKey()); + final Identifier key = BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(entry.getKey()); map.put(SpongeAdventure.asAdventure(key), new SpongeDataComponentValue<>(entry.getValue())); }); return map; } + public static @Nullable Integer asVanillaNullable(@Nullable ShadowColor shadowColor) { + if (shadowColor == null) { + return null; + } + + return shadowColor.value(); + } + private record SpongeDataComponentValue(Optional value) implements DataComponentValue { } // Key - public static ResourceLocation asVanilla(final Key key) { - if ((Object) key instanceof ResourceLocation) { - return (ResourceLocation) (Object) key; + public static Identifier asVanilla(final Key key) { + if ((Object) key instanceof Identifier) { + return (Identifier) (Object) key; } - return ResourceLocation.fromNamespaceAndPath(key.namespace(), key.value()); + return Identifier.fromNamespaceAndPath(key.namespace(), key.value()); } - public static @Nullable ResourceLocation asVanillaNullable(final @Nullable Key key) { + public static @Nullable Identifier asVanillaLocation(final @Nullable Key key) { if (key == null) { return null; } - return SpongeAdventure.asVanilla(key); + return Identifier.fromNamespaceAndPath(key.namespace(), key.value()); + } + + public static @Nullable FontDescription asVanillaFontDescription(final @Nullable Key key) { + if (key == null) { + return null; + } + return new FontDescription.Resource(SpongeAdventure.asVanilla(key)); } // Sound public static Sound.Source asAdventure(final SoundSource source) { - switch (source) { - case MASTER: return Sound.Source.MASTER; - case MUSIC: return Sound.Source.MUSIC; - case RECORDS: return Sound.Source.RECORD; - case WEATHER: return Sound.Source.WEATHER; - case BLOCKS: return Sound.Source.BLOCK; - case HOSTILE: return Sound.Source.HOSTILE; - case NEUTRAL: return Sound.Source.NEUTRAL; - case PLAYERS: return Sound.Source.PLAYER; - case AMBIENT: return Sound.Source.AMBIENT; - case VOICE: return Sound.Source.VOICE; - } - throw new IllegalArgumentException(source.name()); + return switch (source) { + case MASTER -> Sound.Source.MASTER; + case MUSIC -> Sound.Source.MUSIC; + case RECORDS -> Sound.Source.RECORD; + case WEATHER -> Sound.Source.WEATHER; + case BLOCKS -> Sound.Source.BLOCK; + case HOSTILE -> Sound.Source.HOSTILE; + case NEUTRAL -> Sound.Source.NEUTRAL; + case PLAYERS -> Sound.Source.PLAYER; + case AMBIENT -> Sound.Source.AMBIENT; + case VOICE -> Sound.Source.VOICE; + case UI -> Sound.Source.UI; + }; } public static SoundSource asVanilla(final Sound.Source source) { - switch (source) { - case MASTER: return SoundSource.MASTER; - case MUSIC: return SoundSource.MUSIC; - case RECORD: return SoundSource.RECORDS; - case WEATHER: return SoundSource.WEATHER; - case BLOCK: return SoundSource.BLOCKS; - case HOSTILE: return SoundSource.HOSTILE; - case NEUTRAL: return SoundSource.NEUTRAL; - case PLAYER: return SoundSource.PLAYERS; - case AMBIENT: return SoundSource.AMBIENT; - case VOICE: return SoundSource.VOICE; - } - throw new IllegalArgumentException(source.name()); + return switch (source) { + case MASTER -> SoundSource.MASTER; + case MUSIC -> SoundSource.MUSIC; + case RECORD -> SoundSource.RECORDS; + case WEATHER -> SoundSource.WEATHER; + case BLOCK -> SoundSource.BLOCKS; + case HOSTILE -> SoundSource.HOSTILE; + case NEUTRAL -> SoundSource.NEUTRAL; + case PLAYER -> SoundSource.PLAYERS; + case AMBIENT -> SoundSource.AMBIENT; + case VOICE -> SoundSource.VOICE; + case UI -> SoundSource.UI; + }; } public static @Nullable SoundSource asVanillaNullable(final Sound.@Nullable Source source) { @@ -813,17 +1006,6 @@ public static SoundSource asVanilla(final Sound.Source source) { return SpongeAdventure.asVanilla(source); } - public static Iterable unpackAudiences(final Audience audience) { - if (audience instanceof ForwardingAudience) { - final List list = new ArrayList<>(); - for (final Audience subAudience : ((ForwardingAudience) audience).audiences()) { - SpongeAdventure.unpackAudiences(subAudience).forEach(list::add); - } - return list; - } - return Collections.singletonList(audience); - } - public static class Factory implements SpongeComponents.Factory { @Override public @NonNull ClickEvent callbackClickEvent(final @NonNull Consumer callback) { @@ -837,29 +1019,18 @@ public static class Factory implements SpongeComponents.Factory { public final @NonNull Component render( final @NonNull Component component, final @NonNull CommandCause senderContext, - @Nullable Audience viewer, + final @NonNull Audience viewer, final @NonNull DefaultedRegistryReference firstOperation, - final @NonNull DefaultedRegistryReference@NonNull... otherOperations + final @NonNull DefaultedRegistryReference @NonNull ... otherOperations ) { Component output = Objects.requireNonNull(component, "component"); Objects.requireNonNull(senderContext, "senderContext"); - // Unwrap the Audience to an entity - while (viewer instanceof ForwardingAudience.Single && !(viewer instanceof Entity)) { - viewer = ((ForwardingAudience.Single) viewer).audience(); - } - final Entity backing; - if (viewer instanceof Entity) { - backing = (Entity) viewer; - } else { - backing = null; - } - output = ((SpongeResolveOperation) Objects.requireNonNull(firstOperation, "firstOperation").get()) - .resolve(output, senderContext, backing); + .resolve(output, senderContext, viewer); for (final DefaultedRegistryReference ref : otherOperations) { - output = ((SpongeResolveOperation) ref.get()).resolve(output, senderContext, backing); + output = ((SpongeResolveOperation) ref.get()).resolve(output, senderContext, viewer); } return output; } @@ -870,7 +1041,7 @@ public static class Factory implements SpongeComponents.Factory { final @NonNull Component component, final @NonNull CommandCause senderContext, final @NonNull DefaultedRegistryReference firstOperation, - final @NonNull DefaultedRegistryReference@NonNull... otherOperations + final @NonNull DefaultedRegistryReference @NonNull ... otherOperations ) { return this.render(component, senderContext, null, firstOperation, otherOperations); } @@ -879,6 +1050,11 @@ public static class Factory implements SpongeComponents.Factory { public ComponentFlattener flattener() { return ComponentFlattenerProvider.INSTANCE; } + + @Override + public VirtualComponent receiverVirtualComponent(final @NonNull Function apply, final @NonNull String fallbackValue) { + return Component.virtual(Audience.class, new AudienceVirtualComponentRenderer(apply, fallbackValue)); + } } // Boss bar tracking diff --git a/src/main/java/org/spongepowered/common/adventure/SpongeChatTypeBuilder.java b/src/main/java/org/spongepowered/common/adventure/SpongeChatTypeBuilder.java new file mode 100644 index 00000000000..86a80a08da7 --- /dev/null +++ b/src/main/java/org/spongepowered/common/adventure/SpongeChatTypeBuilder.java @@ -0,0 +1,120 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.adventure; + +import net.kyori.adventure.text.format.Style; +import net.minecraft.network.chat.ChatTypeDecoration; +import org.spongepowered.api.adventure.ChatType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public final class SpongeChatTypeBuilder implements ChatType.Builder { + + private String translationKey; + private net.minecraft.network.chat.Style style; + private List parameters; + + private ChatTypeDecoration narration; + + public SpongeChatTypeBuilder() { + this.reset(); + } + + @Override + public ChatType.Builder from(final ChatType value) { + net.minecraft.network.chat.ChatType chatType = (net.minecraft.network.chat.ChatType) (Object) value; + this.translationKey = chatType.chat().translationKey(); + this.parameters = chatType.chat().parameters(); + this.style = chatType.chat().style(); + this.narration = chatType.narration(); + return this; + } + + @Override + public ChatType.Builder translationKey(final String translationKey) { + this.translationKey = translationKey; + return this; + } + + @Override + public ChatType.Builder style(final Style style) { + this.style = SpongeAdventure.asVanilla(style); + return this; + } + + private void addParameter(final ChatTypeDecoration.Parameter parameter) { + if (this.parameters.contains(parameter)) { + // TODO check + // throw new IllegalStateException("Parameter already exists"); + } + this.parameters.add(parameter); + } + + @Override + public ChatType.Builder addSender() { + if (this.parameters == null) { + this.parameters = new ArrayList<>(); + } + this.addParameter(ChatTypeDecoration.Parameter.SENDER); + return this; + } + + @Override + public ChatType.Builder addContent() { + if (this.parameters == null) { + this.parameters = new ArrayList<>(); + } + this.addParameter(ChatTypeDecoration.Parameter.CONTENT); + return this; + } + + @Override + public ChatType.Builder addTarget() { + if (this.parameters == null) { + this.parameters = new ArrayList<>(); + } + this.addParameter(ChatTypeDecoration.Parameter.TARGET); + return this; + } + + @Override + public ChatType.Builder reset() { + this.translationKey = null; + this.style = net.minecraft.network.chat.Style.EMPTY; + this.parameters = null; + this.narration = ChatTypeDecoration.withSender("chat.type.text.narrate"); + return this; + } + + @Override + public ChatType build() { + Objects.requireNonNull(this.translationKey, "name"); + Objects.requireNonNull(this.parameters, "parameter"); + Objects.requireNonNull(this.style, "style"); + return (ChatType) (Object) new net.minecraft.network.chat.ChatType(new ChatTypeDecoration(this.translationKey, this.parameters, this.style), this.narration); + } +} diff --git a/src/main/java/org/spongepowered/common/adventure/SpongeChatTypeTemplate.java b/src/main/java/org/spongepowered/common/adventure/SpongeChatTypeTemplate.java deleted file mode 100644 index bdc5636e3a6..00000000000 --- a/src/main/java/org/spongepowered/common/adventure/SpongeChatTypeTemplate.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.adventure; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.kyori.adventure.text.format.Style; -import net.minecraft.core.RegistryAccess; -import net.minecraft.network.chat.ChatType; -import net.minecraft.network.chat.ChatTypeDecoration; -import net.minecraft.resources.RegistryOps; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.adventure.ChatTypeTemplate; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.function.Function; - -public record SpongeChatTypeTemplate(ResourceKey key, ChatType representedType, DataPack pack) implements ChatTypeTemplate { - - @Override - public org.spongepowered.api.adventure.ChatType type() { - return (org.spongepowered.api.adventure.ChatType) (Object) this.representedType; - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeChatTypeTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized ChatType:\n" + serialized, e); - } - } - - public static JsonElement encode(final ChatTypeTemplate template, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return ChatType.DIRECT_CODEC.encodeStart(ops, (ChatType) (Object) template.type()).getOrThrow(); - } - - public static ChatType decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return ChatType.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static ChatTypeTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final ChatType parsed = SpongeChatTypeTemplate.decode(packEntry, registryAccess); - return new SpongeChatTypeTemplate(key, parsed, pack); - } - - public static final class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - private String translationKey; - private net.minecraft.network.chat.Style style; - private List parameters; - - private ChatTypeDecoration narration; - - public BuilderImpl() { - this.reset(); - } - - @Override - public Builder fromValue(final org.spongepowered.api.adventure.ChatType value) { - ChatType chatType = (ChatType) (Object) value; - this.translationKey = chatType.chat().translationKey(); - this.parameters = chatType.chat().parameters(); - this.style = chatType.chat().style(); - this.narration = chatType.narration(); - return this; - } - - @Override - public Builder translationKey(final String translationKey) { - this.translationKey = translationKey; - return this; - } - - @Override - public Builder style(final Style style) { - this.style = SpongeAdventure.asVanilla(style); - return this; - } - - private void addParameter(final ChatTypeDecoration.Parameter parameter) { - if (this.parameters.contains(parameter)) { - // TODO check - // throw new IllegalStateException("Parameter already exists"); - } - this.parameters.add(parameter); - } - - @Override - public Builder addSender() { - if (this.parameters == null) { - this.parameters = new ArrayList<>(); - } - this.addParameter(ChatTypeDecoration.Parameter.SENDER); - return this; - } - - @Override - public Builder addContent() { - if (this.parameters == null) { - this.parameters = new ArrayList<>(); - } - this.addParameter(ChatTypeDecoration.Parameter.CONTENT); - return this; - } - - @Override - public Builder addTarget() { - if (this.parameters == null) { - this.parameters = new ArrayList<>(); - } - this.addParameter(ChatTypeDecoration.Parameter.TARGET); - return this; - } - - @Override - public Function valueExtractor() { - return ChatTypeTemplate::type; - } - - @Override - public Builder reset() { - this.key = null; - this.pack = DataPacks.CHAT_TYPE; - this.translationKey = null; - this.style = net.minecraft.network.chat.Style.EMPTY; - this.parameters = null; - this.narration = ChatTypeDecoration.withSender("chat.type.text.narrate");; - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - final ChatType chatType = SpongeChatTypeTemplate.decode(json, SpongeCommon.server().registryAccess()); - this.fromValue((org.spongepowered.api.adventure.ChatType) (Object) chatType); - return this; - } - - @Override - protected ChatTypeTemplate build0() { - Objects.requireNonNull(this.translationKey, "name"); - Objects.requireNonNull(this.parameters, "parameter"); - Objects.requireNonNull(this.style, "style"); - final ChatType chatType = new ChatType(new ChatTypeDecoration(this.translationKey, this.parameters, this.style), this.narration); - return new SpongeChatTypeTemplate(this.key, chatType, this.pack); - } - } -} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ResourceType.java b/src/main/java/org/spongepowered/common/adventure/SpongeRendererContext.java similarity index 61% rename from vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ResourceType.java rename to src/main/java/org/spongepowered/common/adventure/SpongeRendererContext.java index adeece5a25d..60d56de5a96 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ResourceType.java +++ b/src/main/java/org/spongepowered/common/adventure/SpongeRendererContext.java @@ -22,29 +22,38 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.applaunch.plugin; +package org.spongepowered.common.adventure; -import cpw.mods.jarhandling.SecureJar; -import org.spongepowered.plugin.PluginResource; +import net.kyori.adventure.audience.Audience; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Locale; -public enum ResourceType { - SERVICE, // service layer - LANGUAGE, // plugin layer - PLUGIN; // game layer +public final class SpongeRendererContext { - public static final String PROPERTY_NAME = "Resource-Type"; + private final Locale locale; + private final @Nullable Audience audience; - public static ResourceType of(final PluginResource resource) { - return ResourceType.fromName(resource.property(PROPERTY_NAME).orElse(null)); + private boolean containsVirtualComponents; + + public SpongeRendererContext(final Locale locale, final @Nullable Audience audience) { + this.locale = locale; + this.audience = audience; + } + + public Locale locale() { + return this.locale; + } + + public @Nullable Audience audience() { + return this.audience; } - public static ResourceType of(final SecureJar jar) { - return ResourceType.fromName(jar.moduleDataProvider().getManifest().getMainAttributes().getValue(PROPERTY_NAME)); + public boolean containsVirtualComponents() { + return this.containsVirtualComponents; } - public static ResourceType fromName(final String name) { - return name == null ? ResourceType.PLUGIN : ResourceType.valueOf(name.toUpperCase(Locale.ROOT)); + public void markContainsVirtualComponents() { + this.containsVirtualComponents = true; } } diff --git a/src/main/java/org/spongepowered/common/adventure/SpongeResolveOperation.java b/src/main/java/org/spongepowered/common/adventure/SpongeResolveOperation.java index f1cffa0dd28..b20edf5091f 100644 --- a/src/main/java/org/spongepowered/common/adventure/SpongeResolveOperation.java +++ b/src/main/java/org/spongepowered/common/adventure/SpongeResolveOperation.java @@ -25,10 +25,14 @@ package org.spongepowered.common.adventure; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.text.Component; import net.kyori.adventure.translation.GlobalTranslator; import net.minecraft.commands.CommandSourceStack; import net.minecraft.network.chat.ComponentUtils; +import net.minecraft.network.chat.ResolutionContext; +import org.apache.commons.lang3.NotImplementedException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.Nullable; @@ -44,7 +48,23 @@ public abstract class SpongeResolveOperation implements ResolveOperation { private static final Logger LOGGER = LogManager.getLogger(); - public abstract Component resolve(final Component input, final CommandCause senderContext, final @Nullable Entity viewer); + public Component resolve(final Component input, final CommandCause senderContext, Audience viewer) { + // Unwrap the Audience to an entity + while (viewer instanceof ForwardingAudience.Single single && !(viewer instanceof Entity)) { + viewer = single.audience(); + } + final @Nullable Entity backing; + if (viewer instanceof Entity entity) { + backing = entity; + } else { + backing = null; + } + return this.resolve(input, senderContext, backing); + } + + public Component resolve(final Component input, final CommandCause senderContext, final @Nullable Entity viewer) { + throw new NotImplementedException(); + } public static SpongeResolveOperation newCustomTranslations() { return new SpongeResolveOperation() { @@ -62,10 +82,15 @@ public static SpongeResolveOperation newContextualComponents() { public Component resolve(final Component input, final CommandCause senderContext, final @Nullable Entity viewer) { try { // TODO: Have a pure-Adventure implementation of this - return SpongeAdventure.asAdventure(ComponentUtils.updateForEntity( - (CommandSourceStack) senderContext, + final var source = (CommandSourceStack) senderContext; + final var builder = ResolutionContext.builder().withSource(source); + if (viewer != null) { + builder.withEntityOverride((net.minecraft.world.entity.Entity) viewer); + } + final var context = builder.build(); + return SpongeAdventure.asAdventure(ComponentUtils.resolve( + context, ((ComponentBridge) input).bridge$asVanillaComponent(), - (net.minecraft.world.entity.Entity) viewer, /* depth = */ 0 )); } catch (final CommandSyntaxException ex) { @@ -76,4 +101,12 @@ public Component resolve(final Component input, final CommandCause senderContext }; } + public static SpongeResolveOperation newVirtualComponents() { + return new SpongeResolveOperation() { + @Override + public Component resolve(final Component input, final CommandCause senderContext, final Audience viewer) { + return SpongeAdventure.SPONGE_VIRTUAL_COMPONENT_RENDERER.render(input, new SpongeRendererContext(null, viewer)); + } + }; + } } diff --git a/src/main/java/org/spongepowered/common/ban/SpongeBanBuilder.java b/src/main/java/org/spongepowered/common/ban/SpongeBanBuilder.java index 19069c6a432..07534861ac7 100644 --- a/src/main/java/org/spongepowered/common/ban/SpongeBanBuilder.java +++ b/src/main/java/org/spongepowered/common/ban/SpongeBanBuilder.java @@ -123,7 +123,7 @@ public Ban build() { if (this.profile == null) { throw new IllegalStateException("User cannot be null"); } - return (Ban) new UserBanListEntry(SpongeGameProfile.toMcProfile(this.profile.withoutProperties()), + return (Ban) new UserBanListEntry(SpongeGameProfile.toNameAndId(this.profile.withoutProperties()), Date.from(this.start), sourceName, this.toDate(this.end), reason); } if (this.address == null) { diff --git a/src/main/java/org/spongepowered/common/block/BlockStateSerializerDeserializer.java b/src/main/java/org/spongepowered/common/block/BlockStateSerializerDeserializer.java index 90903c5dad1..8e7d9204630 100644 --- a/src/main/java/org/spongepowered/common/block/BlockStateSerializerDeserializer.java +++ b/src/main/java/org/spongepowered/common/block/BlockStateSerializerDeserializer.java @@ -43,7 +43,7 @@ public static String serialize(final BlockState state) { public static Optional deserialize(final String string) { final String state = Objects.requireNonNull(string, "Id cannot be null!").toLowerCase(Locale.ENGLISH); try { - final BlockStateParser.BlockResult result = BlockStateParser.parseForBlock(SpongeCommon.vanillaRegistry(Registries.BLOCK).asLookup(), state, true); + final BlockStateParser.BlockResult result = BlockStateParser.parseForBlock(SpongeCommon.vanillaRegistry(Registries.BLOCK), state, true); return Optional.of((BlockState) result.blockState()); } catch (final CommandSyntaxException e) { return Optional.empty(); diff --git a/src/main/java/org/spongepowered/common/block/SpongeBlockSnapshot.java b/src/main/java/org/spongepowered/common/block/SpongeBlockSnapshot.java index 0b0c48012fe..dc57279948b 100644 --- a/src/main/java/org/spongepowered/common/block/SpongeBlockSnapshot.java +++ b/src/main/java/org/spongepowered/common/block/SpongeBlockSnapshot.java @@ -27,10 +27,12 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.storage.TagValueInput; import org.apache.logging.log4j.Level; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; @@ -165,6 +167,7 @@ public BlockSnapshot withLocation(final ServerLocation location) { return SpongeBlockSnapshot.BuilderImpl.pooled().from(this).position(location.blockPosition()).world(location.worldKey()).build(); } + @SuppressWarnings("deprecation") @Override public boolean restore(final boolean force, final BlockChangeFlag flag) { // TODO - rewrite with the PhaseTracker being the hook or use SpongeImplHooks to do the restore. @@ -177,7 +180,7 @@ public boolean restore(final boolean force, final BlockChangeFlag flag) { final ServerLevel world = optionalWorld.get(); // We need to deterministically define the context as nullable if we don't need to enter. // this way we guarantee an exit. - try (final PhaseContext context = BlockPhase.State.RESTORING_BLOCKS.createPhaseContext(PhaseTracker.SERVER)) { + try (final PhaseContext context = BlockPhase.State.RESTORING_BLOCKS.createPhaseContext(PhaseTracker.getWorldInstance(world))) { context.buildAndSwitch(); final BlockPos pos = VecHelper.toBlockPos(this.pos); if (!world.isInWorldBounds(pos)) { // Invalid position. Inline this check @@ -199,7 +202,8 @@ public boolean restore(final boolean force, final BlockChangeFlag flag) { @Nullable BlockEntity te = world.getBlockEntity(pos); if (te != null) { te.setBlockState((net.minecraft.world.level.block.state.BlockState) this.blockState); - te.loadWithComponents(this.compound, world.registryAccess()); + final var input = TagValueInput.create(ProblemReporter.DISCARDING, world.registryAccess(), this.compound); + te.loadWithComponents(input); } else { // Because, some mods will "unintentionally" only obey some of the rules but not all. // In cases like this, we need to directly just say "fuck it" and deserialize from the compound directly. @@ -265,7 +269,11 @@ public Optional createArchetype() { return Optional.empty(); } - final String blockEntityId = this.compound.getString(Constants.Item.BLOCK_ENTITY_ID); + final var blockEntityIDOpt = this.compound.getString(Constants.Item.BLOCK_ENTITY_ID); + if (blockEntityIDOpt.isEmpty()) { + return Optional.empty(); + } + final var blockEntityID = blockEntityIDOpt.get(); final CompoundTag compound = this.compound.copy(); compound.remove(Constants.Sponge.BlockSnapshot.TILE_ENTITY_POSITION_X); @@ -275,7 +283,7 @@ public Optional createArchetype() { return Optional.of(SpongeBlockEntityArchetypeBuilder.pooled() .state(this.state()) - .blockEntity((BlockEntityType) SpongeCommon.vanillaRegistry(Registries.BLOCK_ENTITY_TYPE).get(ResourceLocation.tryParse(blockEntityId))) + .blockEntity((BlockEntityType) SpongeCommon.vanillaRegistry(Registries.BLOCK_ENTITY_TYPE).getOptional(Identifier.tryParse(blockEntityID)).orElse(null)) .blockEntityData(NBTTranslator.INSTANCE.translate(compound)) .build()); } diff --git a/src/main/java/org/spongepowered/common/block/entity/SpongeBlockEntityArchetype.java b/src/main/java/org/spongepowered/common/block/entity/SpongeBlockEntityArchetype.java index f4331bc2054..e9b60d40af3 100644 --- a/src/main/java/org/spongepowered/common/block/entity/SpongeBlockEntityArchetype.java +++ b/src/main/java/org/spongepowered/common/block/entity/SpongeBlockEntityArchetype.java @@ -28,8 +28,10 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.storage.TagValueInput; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.block.BlockState; @@ -91,6 +93,7 @@ public DataContainer blockEntityData() { return NBTTranslator.INSTANCE.translateFrom(this.compound); } + @SuppressWarnings("deprecation") @Override public Optional apply(final ServerLocation location) { final BlockState currentState = location.block(); @@ -113,7 +116,8 @@ public Optional apply(final ServerLocation location) { compound.putInt(Constants.TileEntity.Y_POS, blockpos.getY()); compound.putInt(Constants.TileEntity.Z_POS, blockpos.getZ()); tileEntity.setBlockState((net.minecraft.world.level.block.state.BlockState) currentState); - tileEntity.loadWithComponents(compound, minecraftWorld.registryAccess()); + final var input = TagValueInput.create(ProblemReporter.DISCARDING, minecraftWorld.registryAccess(), compound); + tileEntity.loadWithComponents(input); return Optional.of((org.spongepowered.api.block.entity.BlockEntity) tileEntity); } diff --git a/src/main/java/org/spongepowered/common/bridge/RegistryBackedTrackableBridge.java b/src/main/java/org/spongepowered/common/bridge/RegistryBackedTrackableBridge.java index 369e7f9c982..d565c971fa4 100644 --- a/src/main/java/org/spongepowered/common/bridge/RegistryBackedTrackableBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/RegistryBackedTrackableBridge.java @@ -25,7 +25,7 @@ package org.spongepowered.common.bridge; import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.spongepowered.common.config.tracker.NamespacedCategory; import org.spongepowered.common.config.tracker.TrackerCategory; @@ -38,7 +38,7 @@ public interface RegistryBackedTrackableBridge extends TrackableBridge { void bridge$saveTrackerConfig(); default void bridge$refreshTrackerStates() { - final ResourceLocation key = this.bridge$trackerRegistryBacking().getKey((T) (Object) this); + final Identifier key = this.bridge$trackerRegistryBacking().getKey((T) (Object) this); final String namespace = key.getNamespace(); final String path = key.getPath(); final NamespacedCategory namespacedCategory = this.bridge$trackerCategory().namespacedOrCreate(namespace); diff --git a/src/main/java/org/spongepowered/common/bridge/advancements/AdvancementProgressBridge.java b/src/main/java/org/spongepowered/common/bridge/advancements/AdvancementProgressBridge.java index c62d93423b6..d2a9f78d9b6 100644 --- a/src/main/java/org/spongepowered/common/bridge/advancements/AdvancementProgressBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/advancements/AdvancementProgressBridge.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.bridge.advancements; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.PlayerAdvancements; import org.spongepowered.api.advancement.Advancement; import org.spongepowered.common.advancement.criterion.ImplementationBackedCriterionProgress; @@ -35,13 +35,13 @@ public interface AdvancementProgressBridge { Advancement bridge$getAdvancement(); - ResourceLocation bridge$getAdvancementKey(); + Identifier bridge$getAdvancementKey(); PlayerAdvancements bridge$getPlayerAdvancements(); void bridge$setPlayerAdvancements(PlayerAdvancements playerAdvancements); - void bridge$setAdvancementId(ResourceLocation key); + void bridge$setAdvancementId(Identifier key); void bridge$invalidateAchievedState(); diff --git a/src/main/java/org/spongepowered/common/bridge/block/BlockBridge.java b/src/main/java/org/spongepowered/common/bridge/block/BlockBridge.java index 28100f5f238..abe2566bcf0 100644 --- a/src/main/java/org/spongepowered/common/bridge/block/BlockBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/block/BlockBridge.java @@ -27,14 +27,8 @@ import net.minecraft.world.level.block.Block; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.data.DataManipulator.Immutable; -import org.spongepowered.api.data.Key; import org.spongepowered.api.data.value.Value; -import org.spongepowered.api.event.CauseStackManager; -import org.spongepowered.common.bridge.server.level.ServerLevelBridge; -import java.util.List; -import java.util.Optional; -import java.util.function.BiConsumer; /** * A quasi interface to mix into every possible {@link Block} such that their @@ -52,66 +46,8 @@ * they already know the type they need to focus on.

*/ public interface BlockBridge { - - /** - * Gets all the {@link Immutable}s for the provided - * {@link BlockState}. - * - * @param blockState The block state being passed in - * @return The list of immutable manipulators - */ - List bridge$getManipulators(BlockState blockState); - - /** - * A simple check whether the class is supported by the block or not. - * - * @param immutable The immutable class - * @return True if the data possibly represented by an instance of the class is supported - */ - boolean bridge$supports(Class immutable); - - /** - * Instead of delegating to a block processor, we can delegate to the block - * to retrieve the correct {@link BlockState} if supported. Considering - * block processors would require to know of the blocks themselves, it is - * easier to use the block to understand what data is being offered, - * and the current block state being used. Since all of the data is already - * relatively kept in the block state instance, it is therefor very well - * possible to cycle according to the block instance. - * - * @param blockState The block state to use as a base - * @param key The key to the data - * @param value The value - * @param The type of value, for type checking - * @return The blockstate with the new value, if available and compatible - */ - Optional bridge$getStateWithValue(BlockState blockState, Key> key, E value); - - /** - * Again, another delegate method directly to the block, usually not all - * required, but it does help if the block does support the manipulator - * in the first place. Considering that most manipulators are single - * data typed, it is discernible for the block to easily check what - * data is being offered and therefor validate whether the data is - * allowed for the specific block state. The block state passed in - * is not changed, but rather used as a blueprint so to speak due to - * the various pre-set data that the block state may contain, such as - * red stone power levels, plant types, etc. etc. etc. - * - * @param blockState The block state to base off of - * @param manipulator The manipulator being offered - * @return The block state with the requested data, if available - */ - Optional bridge$getStateWithData(BlockState blockState, Immutable manipulator); - // Normal API methods - boolean bridge$isVanilla(); - - boolean bridge$hasCollideLogic(); - - boolean bridge$hasCollideWithStateLogic(); - /** * Used only for Forge's dummy air block that is acting as a surrogate block for missing * mod blocks. Usually when a block is simply marked for replacement when a mod is re-introduced. @@ -122,8 +58,4 @@ public interface BlockBridge { return false; } - default BiConsumer bridge$getTickFrameModifier() { - return (frame, world) -> { - }; - } } diff --git a/src/main/java/org/spongepowered/common/bridge/block/entity/AbstractFurnaceBlockEntityBridge.java b/src/main/java/org/spongepowered/common/bridge/block/entity/AbstractFurnaceBlockEntityBridge.java index e16bf8271d1..01f2295d218 100644 --- a/src/main/java/org/spongepowered/common/bridge/block/entity/AbstractFurnaceBlockEntityBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/block/entity/AbstractFurnaceBlockEntityBridge.java @@ -33,4 +33,5 @@ public interface AbstractFurnaceBlockEntityBridge { Optional> bridge$getCurrentRecipe(); + void impl$callInterruptSmeltEvent(); } diff --git a/src/main/java/org/spongepowered/common/bridge/commands/CommandSourceStackBridge.java b/src/main/java/org/spongepowered/common/bridge/commands/CommandSourceStackBridge.java index bb0c9c5b666..301d235f029 100644 --- a/src/main/java/org/spongepowered/common/bridge/commands/CommandSourceStackBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/commands/CommandSourceStackBridge.java @@ -29,6 +29,7 @@ import org.spongepowered.api.command.CommandCause; import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.service.permission.PermissionService; import java.util.function.Supplier; @@ -47,4 +48,6 @@ public interface CommandSourceStackBridge { void bridge$setCause(Cause build); void bridge$setPotentialPermissionNode(@Nullable Supplier permission); + + void bridge$permissionService(PermissionService permissionService); } diff --git a/src/main/java/org/spongepowered/common/bridge/core/RegistryBridge.java b/src/main/java/org/spongepowered/common/bridge/core/RegistryBridge.java index 0ffe903bea2..c58561d4cfc 100644 --- a/src/main/java/org/spongepowered/common/bridge/core/RegistryBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/core/RegistryBridge.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.bridge.core; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.registry.RegistryEntry; import org.spongepowered.api.registry.RegistryType; @@ -39,6 +40,8 @@ public interface RegistryBridge { Optional> bridge$get(ResourceKey key); + @Nullable RegistryEntry bridge$getEntry(ResourceKey key); + Stream> bridge$streamEntries(); } diff --git a/src/main/java/org/spongepowered/common/bridge/core/WritableRegistryBridge.java b/src/main/java/org/spongepowered/common/bridge/core/WritableRegistryBridge.java index 3ea7241e75d..cab05649a50 100644 --- a/src/main/java/org/spongepowered/common/bridge/core/WritableRegistryBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/core/WritableRegistryBridge.java @@ -24,10 +24,30 @@ */ package org.spongepowered.common.bridge.core; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.common.registry.InitialRegistryData; + +import java.util.function.Supplier; +import java.util.stream.Stream; + public interface WritableRegistryBridge { boolean bridge$isDynamic(); void bridge$setDynamic(boolean isDynamic); + void bridge$setRegistryHolder(RegistryHolder registryHolder); + + void bridge$addDependencies(Supplier> supplier, RegistryType... dependencies); + + void bridge$addDependencies(Runnable runnable, RegistryType... dependencies); + + Stream> bridge$pendingDependencies(); + + void bridge$markEventCalled(); + + boolean bridge$eventCalled(); + + void bridge$unfreeze(); } diff --git a/src/main/java/org/spongepowered/common/bridge/explosives/ExplosiveBridge.java b/src/main/java/org/spongepowered/common/bridge/explosives/ExplosiveBridge.java index c53341e74bb..34aac1a2009 100644 --- a/src/main/java/org/spongepowered/common/bridge/explosives/ExplosiveBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/explosives/ExplosiveBridge.java @@ -31,7 +31,10 @@ public interface ExplosiveBridge { - Optional bridge$getExplosionRadius(); + Optional bridge$getExplosionRadius(); + + void bridge$setExplosionRadius(@Nullable Float radius); + + default void bridge$cancelExplosion() {} - void bridge$setExplosionRadius(@Nullable Integer radius); } diff --git a/src/main/java/org/spongepowered/common/bridge/network/chat/SpongeChatDecorator.java b/src/main/java/org/spongepowered/common/bridge/network/chat/SpongeChatDecorator.java index 4fc27bc7dfe..ead45659749 100644 --- a/src/main/java/org/spongepowered/common/bridge/network/chat/SpongeChatDecorator.java +++ b/src/main/java/org/spongepowered/common/bridge/network/chat/SpongeChatDecorator.java @@ -42,7 +42,7 @@ public class SpongeChatDecorator implements ChatDecorator { @Override public Component decorate(@Nullable final ServerPlayer player, final Component message) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.SERVER.pushCauseFrame()) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getServerInstanceExplicitly().pushCauseFrame()) { if (player != null) { frame.pushCause(player); } diff --git a/src/main/java/org/spongepowered/common/bridge/server/MinecraftServerBridge.java b/src/main/java/org/spongepowered/common/bridge/server/MinecraftServerBridge.java index 41085b37ef3..0d88b6adafd 100644 --- a/src/main/java/org/spongepowered/common/bridge/server/MinecraftServerBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/server/MinecraftServerBridge.java @@ -24,18 +24,16 @@ */ package org.spongepowered.common.bridge.server; -import com.google.inject.Injector; import net.kyori.adventure.resource.ResourcePackRequest; import net.minecraft.util.thread.BlockableEventLoop; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.Game; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.common.registry.RegistryHolderLogic; import org.spongepowered.common.service.server.SpongeServerScopedServiceProvider; import org.spongepowered.common.user.SpongeUserManager; public interface MinecraftServerBridge { - void bridge$initServices(Game game, Injector injector); - SpongeServerScopedServiceProvider bridge$getServiceProvider(); @Nullable ResourcePackRequest bridge$getResourcePack(); @@ -46,6 +44,12 @@ public interface MinecraftServerBridge { BlockableEventLoop bridge$spongeMainThreadExecutor(); + void bridge$reloadServerRegistries(RegistryHolder holder); + + void bridge$reloadedServerRegistries(RegistryHolderLogic holder); + + RegistryHolderLogic bridge$registryHolder(); + default void bridge$tickServer(int ticks) { throw new UnsupportedOperationException("Cannot trigger manual server tick outside test environment"); } diff --git a/src/main/java/org/spongepowered/common/bridge/server/ReloadableServerResourcesBridge.java b/src/main/java/org/spongepowered/common/bridge/server/ReloadableServerResourcesBridge.java new file mode 100644 index 00000000000..75a0e8e2b74 --- /dev/null +++ b/src/main/java/org/spongepowered/common/bridge/server/ReloadableServerResourcesBridge.java @@ -0,0 +1,35 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.bridge.server; + +import net.minecraft.server.packs.resources.ResourceManager; +import org.checkerframework.checker.nullness.qual.Nullable; + +public interface ReloadableServerResourcesBridge { + + void bridge$resourceManager(ResourceManager resourceManager); + + @Nullable ResourceManager bridge$resourceManager(); +} diff --git a/src/main/java/org/spongepowered/common/bridge/server/level/ServerLevelBridge.java b/src/main/java/org/spongepowered/common/bridge/server/level/ServerLevelBridge.java index d65a7b97618..33344a4fd35 100644 --- a/src/main/java/org/spongepowered/common/bridge/server/level/ServerLevelBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/server/level/ServerLevelBridge.java @@ -24,8 +24,9 @@ */ package org.spongepowered.common.bridge.server.level; +import net.minecraft.network.protocol.game.ClientboundExplodePacket; import net.minecraft.server.bossevents.CustomBossEvents; -import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.world.level.storage.LevelStorageSource; import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.world.explosion.Explosion; @@ -34,14 +35,15 @@ public interface ServerLevelBridge { LevelStorageSource.LevelStorageAccess bridge$getLevelSave(); - ChunkProgressListener bridge$getChunkProgressListener(); - boolean bridge$isLoaded(); CustomBossEvents bridge$getBossBarManager(); void bridge$triggerExplosion(Explosion explosion); + void bridge$handleExplosionPacket(final ServerGamePacketListenerImpl instance, Explosion apiExplosion, + final ClientboundExplodePacket packet); + void bridge$setManualSave(boolean state); BlockSnapshot bridge$createSnapshot(int x, int y, int z); diff --git a/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerBridge.java b/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerBridge.java index ac2c724af1a..3eec12cdcab 100644 --- a/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerBridge.java @@ -92,8 +92,18 @@ public interface ServerPlayerBridge extends ServerPlayerEntityHealthScaleBridge default void bridge$sendChangeDimension(final Holder dimensionType, final ResourceKey key, final long hashedSeed, final GameType gameType, final GameType previousGameType, final boolean isDebug, final boolean isFlat, final byte dataToKeepMask) { - ((ServerPlayer) this).connection.send(new ClientboundRespawnPacket(new CommonPlayerSpawnInfo(dimensionType, key, hashedSeed, gameType, previousGameType, isDebug, - isFlat, ((ServerPlayer) this).getLastDeathLocation(), ((ServerPlayer) this).getPortalCooldown()), dataToKeepMask)); + ((ServerPlayer) this).connection.send(new ClientboundRespawnPacket(new CommonPlayerSpawnInfo( + dimensionType, + key, + hashedSeed, + gameType, + previousGameType, + isDebug, + isFlat, + ((ServerPlayer) this).getLastDeathLocation(), + ((ServerPlayer) this).getPortalCooldown(), + ((ServerPlayer) this).level().getSeaLevel() + ), dataToKeepMask)); } boolean bridge$kick(final Component message); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/enchantment/EnchantmentMixin.java b/src/main/java/org/spongepowered/common/bridge/server/packs/resources/MultiPackResourceManagerBridge.java similarity index 83% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/item/enchantment/EnchantmentMixin.java rename to src/main/java/org/spongepowered/common/bridge/server/packs/resources/MultiPackResourceManagerBridge.java index 7968d13b542..edfc677db05 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/enchantment/EnchantmentMixin.java +++ b/src/main/java/org/spongepowered/common/bridge/server/packs/resources/MultiPackResourceManagerBridge.java @@ -22,12 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.item.enchantment; +package org.spongepowered.common.bridge.server.packs.resources; -import net.minecraft.world.item.enchantment.Enchantment; -import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.registry.RegistryHolderLogic; -@Mixin(Enchantment.class) -public abstract class EnchantmentMixin { +public interface MultiPackResourceManagerBridge { + void bridge$setRegistryHolder(RegistryHolderLogic holder); } diff --git a/src/main/java/org/spongepowered/common/bridge/server/packs/resources/ResourceManagerBridge.java b/src/main/java/org/spongepowered/common/bridge/server/packs/resources/ResourceManagerBridge.java index 47ba38f97f2..da0676dc11e 100644 --- a/src/main/java/org/spongepowered/common/bridge/server/packs/resources/ResourceManagerBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/server/packs/resources/ResourceManagerBridge.java @@ -24,11 +24,16 @@ */ package org.spongepowered.common.bridge.server.packs.resources; +import org.spongepowered.common.service.server.SpongeServerScopedServiceProvider; import org.spongepowered.common.tag.SpongePluginTags; public interface ResourceManagerBridge { void bridge$pluginProvidedTags(SpongePluginTags pluginTags); + void bridge$services(SpongeServerScopedServiceProvider services); + SpongePluginTags bridge$pluginProvidedTags(); + + SpongeServerScopedServiceProvider bridge$services(); } diff --git a/neoforge/src/main/java/org/spongepowered/neoforge/SpongeAPIMod.java b/src/main/java/org/spongepowered/common/bridge/tags/TagBridge.java similarity index 89% rename from neoforge/src/main/java/org/spongepowered/neoforge/SpongeAPIMod.java rename to src/main/java/org/spongepowered/common/bridge/tags/TagBridge.java index f132db9b9f2..332754b65a9 100644 --- a/neoforge/src/main/java/org/spongepowered/neoforge/SpongeAPIMod.java +++ b/src/main/java/org/spongepowered/common/bridge/tags/TagBridge.java @@ -22,10 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge; +package org.spongepowered.common.bridge.tags; -import net.neoforged.fml.common.Mod; +import net.minecraft.tags.TagKey; -@Mod("spongeapi") -public class SpongeAPIMod { +public interface TagBridge { + + TagKey bridge$asVanillaTag(); } diff --git a/src/main/java/org/spongepowered/common/bridge/tags/TagLoaderBridge.java b/src/main/java/org/spongepowered/common/bridge/tags/TagLoaderBridge.java index ae1601faeeb..7c4ab0e3aaa 100644 --- a/src/main/java/org/spongepowered/common/bridge/tags/TagLoaderBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/tags/TagLoaderBridge.java @@ -24,8 +24,9 @@ */ package org.spongepowered.common.bridge.tags; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.core.Registry; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; import net.minecraft.tags.TagEntry; import net.minecraft.tags.TagLoader; import org.checkerframework.checker.nullness.qual.Nullable; @@ -35,9 +36,9 @@ public interface TagLoaderBridge { - void bridge$registryEntry(RegistryAccess.RegistryEntry registryEntry); + void bridge$registryKey(ResourceKey> registryKey); - void bridge$buildingTagKey(@Nullable ResourceLocation key); + void bridge$buildingTagKey(@Nullable Identifier key); default boolean bridge$isAdd(final TagLoader.EntryWithSource entry) { return true; diff --git a/src/main/java/org/spongepowered/common/bridge/world/TrackedWorldBridge.java b/src/main/java/org/spongepowered/common/bridge/world/TrackedWorldBridge.java index fbc9060b94c..049e851f266 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/TrackedWorldBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/TrackedWorldBridge.java @@ -26,25 +26,29 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; -import org.checkerframework.checker.nullness.qual.Nullable; +import net.minecraft.world.phys.BlockHitResult; +import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.world.BlockChangeFlag; -import org.spongepowered.api.world.explosion.Explosion; import org.spongepowered.common.block.SpongeBlockSnapshot; import org.spongepowered.common.bridge.server.level.ServerLevelBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.bridge.world.level.block.state.BlockStateBridge; -import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.context.transaction.TransactionalCaptureSupplier; +import org.spongepowered.common.event.tracking.context.transaction.effect.InteractionAtArgs; +import org.spongepowered.common.event.tracking.context.transaction.effect.UseItemArgs; +import org.spongepowered.common.event.tracking.context.transaction.effect.UseItemAtArgs; +import org.spongepowered.common.event.tracking.context.transaction.pipeline.InteractionPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.WorldPipeline; import java.util.Optional; -import java.util.function.Function; /** * A specialized {@link LevelBridge} or {@link ServerLevelBridge} @@ -53,19 +57,14 @@ */ public interface TrackedWorldBridge { - boolean bridge$forceSpawnEntity(Entity entity); - - net.minecraft.world.level.Explosion tracker$triggerInternalExplosion(Explosion explosion, Function> contextCreator); - Optional bridge$startBlockChange(BlockPos pos, BlockState state, int rawFlags); /** * Delegates to the {@link ServerLevel} to perform the lookup for a {@link LevelChunk} * such that if the target {@link BlockPos} results in a {@code false} for - * {@link ServerLevel#isBlockLoaded(BlockPos)}, {@link BlockSnapshot#empty()} + * {@link ServerLevel#isLoaded(BlockPos)}, {@link BlockSnapshot#empty()} * will be returned. Likewise, optimizes the creation of the snapshot by performing - * the {@link LevelChunk#getBlockState(BlockPos)} and {@link LevelChunk#getTileEntity(BlockPos, Chunk.CreateEntityType)} + * the {@link LevelChunk#getBlockState(BlockPos)} and {@link LevelChunk#getBlockEntity(BlockPos, LevelChunk.EntityCreationType)} * lookup on the same chunk, avoiding an additional chunk lookup. * *

This should be used when the "known" {@link BlockState} for the target @@ -80,17 +79,13 @@ public interface TrackedWorldBridge { } /** - * Creates a {@link BlockSnapshot} but performs an additional {@link LevelChunk#getTileEntity(BlockPos, Chunk.CreateEntityType)} + * Creates a {@link BlockSnapshot} but performs an additional {@link LevelChunk#getBlockEntity(BlockPos, LevelChunk.EntityCreationType)} * lookup if the providing {@link BlockState#getBlock()} {@code instanceof} is * {@code true} for being an {@link EntityBlock} or * {@link BlockStateBridge#bridge$hasTileEntity()}, and associates * the resulting snapshot of said Tile with the snapshot. This is useful for in-progress * snapshot creation during transaction building for {@link TransactionalCaptureSupplier}. * - *

If the {@link BlockEntity} is already known, and no lookups are needed, use - * {@link #bridge$createSnapshotWithEntity(BlockState, BlockPos, BlockChangeFlag, BlockEntity)} as it avoids - * any further chunk lookups.

- * * @param state The block state * @param pos The target position * @param updateFlag The update flag @@ -98,17 +93,11 @@ public interface TrackedWorldBridge { */ SpongeBlockSnapshot bridge$createSnapshot(BlockState state, BlockPos pos, BlockChangeFlag updateFlag); - /** - * Similar to {@link #bridge$createSnapshot(BlockState, BlockPos, BlockChangeFlag)}, - * but with the added avoidance of a {@link BlockEntity} lookup during the creation of the resulting - * {@link SpongeBlockSnapshot}. - * - * @param state The state - * @param pos The position - * @param updateFlag The update flag - * @param tileEntity The tile entity to serialize, if available - * @return The snapshot, never NONE - */ - SpongeBlockSnapshot bridge$createSnapshotWithEntity(BlockState state, BlockPos pos, BlockChangeFlag updateFlag, @Nullable BlockEntity tileEntity); + InteractionPipeline<@NonNull InteractionAtArgs> bridge$startInteractionUseOnChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, BlockHitResult blockRaytraceResultIn, BlockState blockstate, ItemStack copiedStack); + + InteractionPipeline<@NonNull InteractionAtArgs> bridge$startInteractionChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, BlockHitResult blockRaytraceResultIn, BlockState blockstate, ItemStack copiedStack); + + InteractionPipeline<@NonNull UseItemAtArgs> bridge$startItemInteractionChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, ItemStack copiedStack, BlockHitResult blockRaytraceResult, boolean creative); + InteractionPipeline<@NonNull UseItemArgs> bridge$startItemInteractionUseChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, ItemStack copiedStack); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/entity/EntityBridge.java b/src/main/java/org/spongepowered/common/bridge/world/entity/EntityBridge.java index a8b41003978..dd3e672ba4c 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/entity/EntityBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/entity/EntityBridge.java @@ -25,11 +25,8 @@ package org.spongepowered.common.bridge.world.entity; import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.portal.DimensionTransition; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.event.cause.entity.DismountType; -import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.common.event.tracking.phase.tick.EntityTickContext; import org.spongepowered.math.vector.Vector3d; @@ -69,10 +66,4 @@ public interface EntityBridge { } boolean bridge$dismountRidingEntity(DismountType type); - - Entity bridge$changeDimension(DimensionTransition transition); - - ChangeEntityWorldEvent.Reposition bridge$fireRepositionEvent(org.spongepowered.api.world.server.ServerWorld originalDestinationWorld, - org.spongepowered.api.world.server.ServerWorld targetWorld, - Vector3d destinationPosition); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/entity/TrackedAttackBridge.java b/src/main/java/org/spongepowered/common/bridge/world/entity/TrackedAttackBridge.java new file mode 100644 index 00000000000..debf57e2a62 --- /dev/null +++ b/src/main/java/org/spongepowered/common/bridge/world/entity/TrackedAttackBridge.java @@ -0,0 +1,39 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.bridge.world.entity; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.common.event.cause.entity.damage.SpongeAttackTracker; + +public interface TrackedAttackBridge { + + @Nullable + SpongeAttackTracker attack$tracker(); + + // Neo hook + default double attack$interactionRangeSquared() { + return 9; + } +} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/entity/ChangeEntityWorldEvent_PreMixin_Forge.java b/src/main/java/org/spongepowered/common/bridge/world/entity/TrackedDamageBridge.java similarity index 57% rename from forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/entity/ChangeEntityWorldEvent_PreMixin_Forge.java rename to src/main/java/org/spongepowered/common/bridge/world/entity/TrackedDamageBridge.java index 30a236eefe4..6ba9a52c8f3 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/entity/ChangeEntityWorldEvent_PreMixin_Forge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/entity/TrackedDamageBridge.java @@ -22,28 +22,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.mixin.core.api.event.entity; +package org.spongepowered.common.bridge.world.entity; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraftforge.event.entity.EntityTravelToDimensionEvent; -import net.minecraftforge.eventbus.api.Event; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.forge.mixin.core.api.event.EventMixin_Forge; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; -import java.util.Collection; -import java.util.Collections; +public interface TrackedDamageBridge { -@Mixin(ChangeEntityWorldEvent.Pre.class) -public interface ChangeEntityWorldEvent_PreMixin_Forge extends EventMixin_Forge { + @Nullable + SpongeDamageTracker damage$tracker(); - @Override - default @Nullable Collection bridge$createForgeEvents() { - final ChangeEntityWorldEvent.Pre thisEvent = (ChangeEntityWorldEvent.Pre) this; - return Collections.singleton( - new EntityTravelToDimensionEvent((Entity) thisEvent.entity(), ((ServerLevel) thisEvent.destinationWorld()).dimension())); + default float damage$firePostEvent(float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + if (tracker == null) { + return damage; + } + damage = tracker.endStep(DamageStepTypes.ABSORPTION, damage); + damage = tracker.callDamagePostEvent((org.spongepowered.api.entity.Entity) this, damage); + this.damage$setContainerDamage(damage); + return damage; } + // Neo hook + default void damage$setContainerDamage(final float damage) {} + + // Neo hook + default float damage$getContainerDamage(final float damage) { + return damage; + } } diff --git a/src/main/java/org/spongepowered/common/bridge/world/entity/boss/enderdragon/EndCrystalBridge.java b/src/main/java/org/spongepowered/common/bridge/world/entity/boss/enderdragon/EndCrystalBridge.java index 931c19522f0..9f58f8701eb 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/entity/boss/enderdragon/EndCrystalBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/entity/boss/enderdragon/EndCrystalBridge.java @@ -25,13 +25,10 @@ package org.spongepowered.common.bridge.world.entity.boss.enderdragon; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Explosion; -import net.minecraft.world.level.Level; import org.checkerframework.checker.nullness.qual.Nullable; public interface EndCrystalBridge { - @Nullable Explosion bridge$throwExplosionEventAndExplode(Level world, @Nullable Entity nil, double x, - double y, double z, boolean smoking, @Nullable DamageSource source); + void bridge$wrappedExplode(double x, + double y, double z, @Nullable DamageSource source, final DamageSource causeSource); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/entity/player/PlayerBridge.java b/src/main/java/org/spongepowered/common/bridge/world/entity/player/PlayerBridge.java index 9fc379537ab..22a36c4021f 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/entity/player/PlayerBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/entity/player/PlayerBridge.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.bridge.world.entity.player; +import com.mojang.authlib.GameProfile; import net.kyori.adventure.bossbar.BossBar; public interface PlayerBridge { @@ -43,4 +44,6 @@ public interface PlayerBridge { void bridge$addActiveBossBar(BossBar bar); void bridge$removeActiveBossBar(BossBar bar); + + void bridge$setGameProfile(GameProfile profile); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/entity/projectile/LargeFireballBridge.java b/src/main/java/org/spongepowered/common/bridge/world/entity/projectile/LargeFireballBridge.java index 95d1f438633..b2282c0c20d 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/entity/projectile/LargeFireballBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/entity/projectile/LargeFireballBridge.java @@ -24,13 +24,9 @@ */ package org.spongepowered.common.bridge.world.entity.projectile; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Explosion; import net.minecraft.world.level.Level; -import org.checkerframework.checker.nullness.qual.Nullable; public interface LargeFireballBridge { - @Nullable Explosion bridge$throwExplosionEventAndExplode(Level worldObj, @Nullable Entity nil, - double x, double y, double z, float strength, boolean flaming, Level.ExplosionInteraction mode); + void bridge$wrappedExplode(double x, double y, double z, float strength, boolean flaming, Level.ExplosionInteraction mode); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/entity/projectile/WitherSkullBridge.java b/src/main/java/org/spongepowered/common/bridge/world/entity/projectile/WitherSkullBridge.java index 8e87c427a77..78301e468b1 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/entity/projectile/WitherSkullBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/entity/projectile/WitherSkullBridge.java @@ -24,13 +24,10 @@ */ package org.spongepowered.common.bridge.world.entity.projectile; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Explosion; import net.minecraft.world.level.Level; -import org.checkerframework.checker.nullness.qual.Nullable; public interface WitherSkullBridge { - @Nullable Explosion bridge$throwExplosionEventAndExplosde(net.minecraft.world.level.Level worldObj, Entity self, double x, + void bridge$wrappedExplode(double x, double y, double z, float strength, boolean flaming, Level.ExplosionInteraction mode); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/entity/vehicle/BoatBridge.java b/src/main/java/org/spongepowered/common/bridge/world/entity/vehicle/AbstractBoatBridge.java similarity index 97% rename from src/main/java/org/spongepowered/common/bridge/world/entity/vehicle/BoatBridge.java rename to src/main/java/org/spongepowered/common/bridge/world/entity/vehicle/AbstractBoatBridge.java index d39353485f8..a7318718bab 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/entity/vehicle/BoatBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/entity/vehicle/AbstractBoatBridge.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.bridge.world.entity.vehicle; -public interface BoatBridge { +public interface AbstractBoatBridge { double bridge$getMaxSpeed(); diff --git a/src/main/java/org/spongepowered/common/item/recipe/ResultFunctionRecipe.java b/src/main/java/org/spongepowered/common/bridge/world/item/crafting/CookingRecipeBridge.java similarity index 85% rename from src/main/java/org/spongepowered/common/item/recipe/ResultFunctionRecipe.java rename to src/main/java/org/spongepowered/common/bridge/world/item/crafting/CookingRecipeBridge.java index 18417201494..61229a08638 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/ResultFunctionRecipe.java +++ b/src/main/java/org/spongepowered/common/bridge/world/item/crafting/CookingRecipeBridge.java @@ -22,11 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.item.recipe; +package org.spongepowered.common.bridge.world.item.crafting; -import java.util.Optional; +import net.minecraft.world.item.crafting.Ingredient; -public interface ResultFunctionRecipe { - Optional resultFunctionId(); +public interface CookingRecipeBridge extends RecipeResultBridge { + + Ingredient bridge$ingredient(); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/item/crafting/SmithingRecipeBridge.java b/src/main/java/org/spongepowered/common/bridge/world/item/crafting/SmithingRecipeBridge.java index 1d63566f01b..74960e4559b 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/item/crafting/SmithingRecipeBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/item/crafting/SmithingRecipeBridge.java @@ -26,10 +26,12 @@ import net.minecraft.world.item.crafting.Ingredient; +import java.util.Optional; + public interface SmithingRecipeBridge extends RecipeResultBridge { - Ingredient bridge$template(); + Optional bridge$template(); Ingredient bridge$base(); - Ingredient bridge$addition(); + Optional bridge$addition(); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/level/ExplosionBridge.java b/src/main/java/org/spongepowered/common/bridge/world/level/ExplosionBridge.java index 68f67ee2665..a83c1171fa5 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/level/ExplosionBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/level/ExplosionBridge.java @@ -24,11 +24,10 @@ */ package org.spongepowered.common.bridge.world.level; + public interface ExplosionBridge { - boolean bridge$getShouldDamageBlocks(); - void bridge$setShouldBreakBlocks(boolean shouldBreakBlocks); boolean bridge$getShouldPlaySmoke(); diff --git a/src/main/java/org/spongepowered/common/bridge/world/level/block/TrackableBlockBridge.java b/src/main/java/org/spongepowered/common/bridge/world/level/block/TrackableBlockBridge.java index 8c8a4418464..719aaf81726 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/level/block/TrackableBlockBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/level/block/TrackableBlockBridge.java @@ -26,8 +26,6 @@ public interface TrackableBlockBridge { - boolean bridge$overridesNeighborNotificationLogic(); - boolean bridge$hasEntityInsideLogic(); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/level/chunk/storage/SerializableChunkDataBridge.java b/src/main/java/org/spongepowered/common/bridge/world/level/chunk/storage/SerializableChunkDataBridge.java new file mode 100644 index 00000000000..815da991b19 --- /dev/null +++ b/src/main/java/org/spongepowered/common/bridge/world/level/chunk/storage/SerializableChunkDataBridge.java @@ -0,0 +1,50 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.bridge.world.level.chunk.storage; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.chunk.LevelChunk; +import org.spongepowered.api.world.SerializationBehavior; + +public interface SerializableChunkDataBridge { + + void bridge$setTrackerData(LevelChunk chunkAccess); + + void bridge$writeTrackerData(CompoundTag level); + + void bridge$parseTrackerData(CompoundTag tag); + + void bridge$writeDataHolderData(CompoundTag level); + + void bridge$setDataHolderData(LevelChunk levelChunk); + + void bridge$parseDataHolderData(CompoundTag tag); + + void bridge$readTrackerDataFrom(LevelChunk levelChunk); + + void bridge$readDataHolderDataFrom(LevelChunk levelChunk); + + void bridge$setSerializationBehavior(SerializationBehavior serializationBehavior); +} diff --git a/src/main/java/org/spongepowered/common/bridge/world/level/dimension/LevelStemBridge.java b/src/main/java/org/spongepowered/common/bridge/world/level/dimension/LevelStemBridge.java index 087a0ed95ea..23eb8b0dc3c 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/level/dimension/LevelStemBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/level/dimension/LevelStemBridge.java @@ -29,7 +29,6 @@ import net.minecraft.world.level.GameType; import net.minecraft.world.level.dimension.LevelStem; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.data.DataManipulator; import org.spongepowered.api.world.SerializationBehavior; import org.spongepowered.common.world.server.SpongeWorldTemplate; import org.spongepowered.math.vector.Vector3i; @@ -62,7 +61,5 @@ public interface LevelStemBridge { LevelStem bridge$decorateData(SpongeWorldTemplate.SpongeDataSection data); - LevelStem bridge$decorateData(DataManipulator data); - - SpongeWorldTemplate.SpongeDataSection bridge$createData(); + boolean bridge$hasLegacyData(); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/level/storage/DimensionDataStorageBridge.java b/src/main/java/org/spongepowered/common/bridge/world/level/storage/DimensionDataStorageBridge.java index 684daaf16c3..836bf6c5624 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/level/storage/DimensionDataStorageBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/level/storage/DimensionDataStorageBridge.java @@ -24,12 +24,12 @@ */ package org.spongepowered.common.bridge.world.level.storage; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.Nullable; public interface DimensionDataStorageBridge { - void bridge$dimensionKey(@Nullable ResourceLocation dimensionKey); + void bridge$dimensionKey(@Nullable Identifier dimensionKey); - @Nullable ResourceLocation bridge$dimensionKey(); + @Nullable Identifier bridge$dimensionKey(); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/level/storage/LevelStorageAccessBridge.java b/src/main/java/org/spongepowered/common/bridge/world/level/storage/LevelStorageAccessBridge.java deleted file mode 100644 index 5d717840102..00000000000 --- a/src/main/java/org/spongepowered/common/bridge/world/level/storage/LevelStorageAccessBridge.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.bridge.world.level.storage; - -public interface LevelStorageAccessBridge { - - void bridge$setDedicated(boolean dedicated); -} diff --git a/src/main/java/org/spongepowered/common/bridge/world/level/storage/PrimaryLevelDataBridge.java b/src/main/java/org/spongepowered/common/bridge/world/level/storage/PrimaryLevelDataBridge.java index eed8045ff8b..102f4e1353d 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/level/storage/PrimaryLevelDataBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/level/storage/PrimaryLevelDataBridge.java @@ -25,10 +25,7 @@ package org.spongepowered.common.bridge.world.level.storage; import com.google.common.collect.BiMap; -import com.mojang.serialization.Dynamic; import net.kyori.adventure.text.Component; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; import net.minecraft.world.Difficulty; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; @@ -67,8 +64,4 @@ public interface PrimaryLevelDataBridge extends ServerLevelDataBridge { void bridge$hardcore(boolean hardcore); void bridge$allowCommands(boolean commands); - - void bridge$readSpongeLevelData(Dynamic impl$spongeLevelData); - - CompoundTag bridge$writeSpongeLevelData(); } diff --git a/src/main/java/org/spongepowered/common/bridge/world/level/storage/ServerLevelDataBridge.java b/src/main/java/org/spongepowered/common/bridge/world/level/storage/ServerLevelDataBridge.java index 1200f059f8d..a4eebdd6265 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/level/storage/ServerLevelDataBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/level/storage/ServerLevelDataBridge.java @@ -26,15 +26,20 @@ import com.google.common.collect.MapMaker; import net.kyori.adventure.text.Component; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.storage.LevelData; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; import org.spongepowered.api.world.SerializationBehavior; import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.bridge.world.level.border.WorldBorderBridge; import org.spongepowered.common.world.server.SpongeServerLevelData; import org.spongepowered.common.world.server.SpongeWorldManager; @@ -80,6 +85,15 @@ public interface ServerLevelDataBridge { return null; } + default @Nullable ChunkGenerator bridge$chunkGenerator() { + return null; + } + + default LevelStem bridge$levelStem() { + final Registry registry = SpongeCommon.vanillaRegistry(Registries.DIMENSION_TYPE); + return new LevelStem(registry.wrapAsHolder(this.bridge$dimensionType()), this.bridge$chunkGenerator()); + } + default boolean bridge$customDifficulty() { return false; } @@ -116,6 +130,15 @@ public interface ServerLevelDataBridge { return Optional.empty(); } + default @Nullable WorldBorderBridge bridge$worldBorder() { + final ServerLevel level = this.bridge$level(); + if (level != null) { + return (WorldBorderBridge) level.getWorldBorder(); + } + + return null; + } + default void bridge$triggerViewDistanceLogic() { final ServerLevel level = this.bridge$level(); if (level != null) { diff --git a/src/main/java/org/spongepowered/common/bridge/world/server/TicketBridge.java b/src/main/java/org/spongepowered/common/bridge/world/server/TicketBridge.java index e3903905cfb..5feb31c4a64 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/server/TicketBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/server/TicketBridge.java @@ -34,6 +34,6 @@ public interface TicketBridge { org.spongepowered.api.world.server.Ticket bridge$retrieveAppropriateTicket(); - void bridge$setParentTicket(Ticket parentTicket); + void bridge$setParentTicket(Ticket parentTicket); } diff --git a/src/main/java/org/spongepowered/common/command/SpongeParameterizedCommand.java b/src/main/java/org/spongepowered/common/command/SpongeParameterizedCommand.java index 4fc69d8a08c..d8378d7e3ef 100644 --- a/src/main/java/org/spongepowered/common/command/SpongeParameterizedCommand.java +++ b/src/main/java/org/spongepowered/common/command/SpongeParameterizedCommand.java @@ -29,6 +29,7 @@ import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.tree.LiteralCommandNode; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; import net.minecraft.commands.CommandSourceStack; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -40,6 +41,7 @@ import org.spongepowered.api.command.parameter.CommandContext; import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.command.parameter.managed.Flag; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.common.command.brigadier.SpongeParameterTranslator; import org.spongepowered.common.command.brigadier.SpongeStringReader; import org.spongepowered.common.command.brigadier.dispatcher.SpongeCommandDispatcher; @@ -66,6 +68,7 @@ public final class SpongeParameterizedCommand implements Command.Parameterized { private final @Nullable CommandExecutor executor; private final boolean isTerminal; private @Nullable SpongeCommandManager commandManager; + private @Nullable RegistryHolder registryHolder; private @Nullable SpongeCommandDispatcher cachedDispatcher; SpongeParameterizedCommand( @@ -87,9 +90,10 @@ public final class SpongeParameterizedCommand implements Command.Parameterized { this.isTerminal = isTerminal; } - public void setCommandManager(final SpongeCommandManager commandManager) { + public void setCommandManager(final SpongeCommandManager commandManager, final RegistryHolder registryHolder) { this.cachedDispatcher = null; this.commandManager = commandManager; + this.registryHolder = registryHolder; } @Override @@ -121,7 +125,7 @@ public boolean canExecute(final @NonNull CommandCause cause) { final Collection usage = Arrays.stream(this.getCachedDispatcher().getAllUsage(this.getCachedDispatcher().getRoot(), (CommandSourceStack) cause, true)) .map(Component::text).collect(Collectors.toList()); - return Component.join(Component.newline(), usage); + return Component.join(JoinConfiguration.separator(Component.newline()), usage); } @Override @@ -172,10 +176,10 @@ private SpongeCommandDispatcher getCachedDispatcher() { } public LiteralCommandNode buildWithAlias(final String primaryAlias) { - return this.buildWithAliases(Collections.singleton(primaryAlias)).iterator().next(); + return this.buildWithAliases(this.registryHolder, Collections.singleton(primaryAlias)).iterator().next(); } - public Collection> buildWithAliases(final Collection aliases) { - return SpongeParameterTranslator.INSTANCE.createCommandTree(this, aliases); + public Collection> buildWithAliases(final RegistryHolder registryHolder, final Collection aliases) { + return SpongeParameterTranslator.INSTANCE.createCommandTree(registryHolder, this, aliases); } } diff --git a/src/main/java/org/spongepowered/common/command/brigadier/SpongeParameterTranslator.java b/src/main/java/org/spongepowered/common/command/brigadier/SpongeParameterTranslator.java index f2871715863..69229d9874a 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/SpongeParameterTranslator.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/SpongeParameterTranslator.java @@ -33,6 +33,7 @@ import org.spongepowered.api.command.Command; import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.command.parameter.managed.Flag; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.common.command.brigadier.argument.ArgumentParser; import org.spongepowered.common.command.brigadier.argument.CustomArgumentParser; import org.spongepowered.common.command.brigadier.tree.SpongeArgumentCommandNode; @@ -65,7 +66,8 @@ private SpongeParameterTranslator() { } @SuppressWarnings({"unchecked", "rawtypes"}) - public Collection> createCommandTree(final Command.Parameterized command, final Collection aliases) { + public Collection> createCommandTree(final RegistryHolder registryHolder, + final Command.Parameterized command, final Collection aliases) { if (aliases.isEmpty()) { throw new IllegalArgumentException("Aliases must not be empty."); } @@ -86,16 +88,16 @@ public Collection> createCommandTree(fina final SpongeLiteralCommandNode commandNode = new SpongeLiteralCommandNode(basicNode); if (executor != null) { - this.createAndAttachNode(Collections.singleton(commandNode), command.parameters(), executor, true, true); + this.createAndAttachNode(registryHolder, Collections.singleton(commandNode), command.parameters(), executor, true, true); } for (final Parameter.Subcommand subcommand : command.subcommands()) { final Collection> builtSubcommand = - this.createCommandTree(subcommand.command(), subcommand.aliases()); + this.createCommandTree(registryHolder, subcommand.command(), subcommand.aliases()); builtSubcommand.forEach(commandNode::addChild); } - this.createFlags(commandNode, command.flags(), executor); + this.createFlags(registryHolder, commandNode, command.flags(), executor); final List> allCommandNodes = new ArrayList<>(); allCommandNodes.add(commandNode); @@ -126,7 +128,7 @@ public Collection> createCommandTree(fina } @SuppressWarnings({"unchecked", "rawtypes"}) - private void createFlags(final LiteralCommandNode node, final List flags, + private void createFlags(final RegistryHolder registryHolder, final LiteralCommandNode node, final List flags, final @Nullable SpongeCommandExecutorWrapper wrapper) { final Collection> nodesToAddChildrenTo = new ArrayList<>(); for (final Flag flag : flags) { @@ -138,6 +140,7 @@ private void createFlags(final LiteralCommandNode node, fina final SpongeFlagLiteralCommandNode flagNode = new SpongeFlagLiteralCommandNode(flagLiteral, flag); if (flag.associatedParameter().isPresent()) { toBeRedirected = this.createAndAttachNode( + registryHolder, Collections.singleton(flagNode), Collections.singletonList(flag.associatedParameter().get()), wrapper, @@ -184,6 +187,7 @@ private void createFlags(final LiteralCommandNode node, fina } private Collection> createAndAttachNode( + final RegistryHolder registryHolder, final Collection> parents, final List children, final @Nullable SpongeCommandExecutorWrapper executorWrapper, @@ -210,6 +214,7 @@ private Collection> createAndAttachNod // If the child is a subcommand, get the subcommand and attach it. At this point, // we're done with the chain and we break out. final Collection> nodes = this.createCommandTree( + registryHolder, subcommands.command(), subcommands.aliases() ); @@ -231,6 +236,7 @@ private Collection> createAndAttachNod parametersToAttachTo = new ArrayList<>(); for (final Parameter p : ((SpongeFirstOfParameter) parameter).childParameters()) { final Collection> branchNodesToAttachTo = this.createAndAttachNode( + registryHolder, nodesToAttachTo, Collections.singletonList(p), executorWrapper, @@ -241,6 +247,7 @@ private Collection> createAndAttachNod } else { // not so fancy stuff, it's a sequence, returns the terminal nodes of the block parametersToAttachTo = new ArrayList<>(this.createAndAttachNode( + registryHolder, nodesToAttachTo, ((SpongeMultiParameter) parameter).childParameters(), executorWrapper, @@ -278,7 +285,7 @@ private Collection> createAndAttachNod key = key + suffix; } final SpongeArgumentCommandNodeBuilder thisNode = SpongeParameterTranslator - .createArgumentNodeBuilders(valueParameter, suffix == null ? null : suffix.toString()); + .createArgumentNodeBuilders(registryHolder, valueParameter, suffix == null ? null : suffix.toString()); if (isTerminal) { thisNode.executes(executorWrapper); @@ -334,20 +341,24 @@ private Collection> createAndAttachNod @SuppressWarnings({"unchecked", "rawtypes"}) private static @NonNull SpongeArgumentCommandNodeBuilder createArgumentNodeBuilders( + final RegistryHolder registryHolder, final SpongeParameterValue parameter, final @Nullable String suffix) { ArgumentParser type = parameter.argumentTypeIfStandard(); if (type == null) { - type = new CustomArgumentParser<>(parameter.parsers(), parameter.completer(), false); + type = new CustomArgumentParser<>( + (List) parameter.parsers().stream().map(p -> p.bind(registryHolder)).toList(), parameter.completer().bind(registryHolder), false); + } else { + type = type.bind(registryHolder); } final SpongeArgumentCommandNodeBuilder argumentBuilder = new SpongeArgumentCommandNodeBuilder<>( SpongeParameterKey.getSpongeKey(parameter.key()), type, - parameter.completer(), - parameter.modifier().orElse(null), + parameter.completer().bind(registryHolder), + parameter.modifier().map(m -> m.bind(registryHolder)).orElse(null), parameter.valueUsage().orElse(null), suffix, parameter.isOptional() diff --git a/src/main/java/org/spongepowered/common/command/brigadier/SpongeStringReader.java b/src/main/java/org/spongepowered/common/command/brigadier/SpongeStringReader.java index a844ac71e65..b569a7998bc 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/SpongeStringReader.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/SpongeStringReader.java @@ -28,8 +28,9 @@ import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.kyori.adventure.text.Component; +import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.TagParser; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; @@ -146,12 +147,12 @@ public float parseFloat() throws ArgumentParseException { @Override public ResourceKey parseResourceKey() throws ArgumentParseException { - return this.readResourceLocation(null); + return this.readIdentifier(null); } @Override public ResourceKey parseResourceKey(final @NonNull String defaultNamespace) throws ArgumentParseException { - return this.readResourceLocation(defaultNamespace); + return this.readIdentifier(defaultNamespace); } @Override @@ -197,7 +198,8 @@ public boolean parseBoolean() throws ArgumentParseException { public String parseNBTString() throws ArgumentParseException { final int startCursor = this.getCursor(); try { - new TagParser(this).readStruct(); + TagParser.create(NbtOps.INSTANCE) + .parseAsArgument(this); } catch (final CommandSyntaxException ex) { this.setCursor(startCursor); throw new ArgumentParseException( @@ -213,7 +215,7 @@ public String parseNBTString() throws ArgumentParseException { @Override public DataContainer parseDataContainer() throws ArgumentParseException { try { - return NBTTranslator.INSTANCE.translate(new TagParser(this).readStruct()); + return NBTTranslator.INSTANCE.translate(TagParser.parseCompoundAsArgument(this)); } catch (final CommandSyntaxException e) { throw this.createException(SpongeAdventure.asAdventure(e.getRawMessage())); } @@ -242,10 +244,10 @@ public void setState(final @NonNull ArgumentReader state) throws IllegalArgument return new ArgumentParseException(errorMessage, inner, this.input(), this.getCursor()); } - private ResourceKey readResourceLocation(final @Nullable String defaultNamespace) throws ArgumentParseException { + private ResourceKey readIdentifier(final @Nullable String defaultNamespace) throws ArgumentParseException { final int i = this.getCursor(); - while (this.canRead() && ResourceLocation.isAllowedInResourceLocation(this.peek())) { + while (this.canRead() && Identifier.isAllowedInIdentifier(this.peek())) { this.skip(); } diff --git a/src/main/java/org/spongepowered/common/command/brigadier/argument/AbstractArgumentParser.java b/src/main/java/org/spongepowered/common/command/brigadier/argument/AbstractArgumentParser.java index 5d78764b697..8885d70cedd 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/argument/AbstractArgumentParser.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/argument/AbstractArgumentParser.java @@ -36,6 +36,7 @@ import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.command.parameter.managed.ValueParameter; import org.spongepowered.api.command.parameter.managed.ValueParameterModifier; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.common.command.brigadier.SpongeStringReader; import org.spongepowered.common.command.brigadier.context.SpongeCommandContext; import org.spongepowered.common.command.brigadier.context.SpongeCommandContextBuilder; @@ -94,4 +95,8 @@ public boolean doesNotRead() { return false; } + @Override + public AbstractArgumentParser bind(final RegistryHolder registryHolder) { + return this; + } } diff --git a/src/main/java/org/spongepowered/common/command/brigadier/argument/ArgumentParser.java b/src/main/java/org/spongepowered/common/command/brigadier/argument/ArgumentParser.java index 3dc705b38af..581e8684304 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/argument/ArgumentParser.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/argument/ArgumentParser.java @@ -33,6 +33,7 @@ import org.spongepowered.api.command.exception.ArgumentParseException; import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.command.parameter.managed.ValueParameterModifier; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.common.command.brigadier.SpongeStringReader; import org.spongepowered.common.command.brigadier.context.SpongeCommandContextBuilder; import org.spongepowered.common.util.Constants; @@ -90,4 +91,7 @@ default boolean hasClientNativeCompletions() { return false; } + default ArgumentParser bind(RegistryHolder registryHolder) { + return this; + } } diff --git a/src/main/java/org/spongepowered/common/command/brigadier/argument/BindableValueParameter.java b/src/main/java/org/spongepowered/common/command/brigadier/argument/BindableValueParameter.java new file mode 100644 index 00000000000..b3f4fdf0dc1 --- /dev/null +++ b/src/main/java/org/spongepowered/common/command/brigadier/argument/BindableValueParameter.java @@ -0,0 +1,70 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.command.brigadier.argument; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.command.CommandCompletion; +import org.spongepowered.api.command.exception.ArgumentParseException; +import org.spongepowered.api.command.parameter.ArgumentReader; +import org.spongepowered.api.command.parameter.CommandContext; +import org.spongepowered.api.command.parameter.Parameter; +import org.spongepowered.api.command.parameter.managed.ValueParameter; +import org.spongepowered.api.command.parameter.managed.standard.ResourceKeyedValueParameter; +import org.spongepowered.api.registry.RegistryHolder; + +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +public final class BindableValueParameter implements ResourceKeyedValueParameter { + + private final ResourceKey key; + private final Function> function; + + public BindableValueParameter(final ResourceKey key, final Function> function) { + this.key = key; + this.function = function; + } + + @Override + public ValueParameter bind(final RegistryHolder registryHolder) { + return this.function.apply(registryHolder); + } + + @Override + public List complete(final CommandContext context, final String currentInput) { + return List.of(); + } + + @Override + public Optional parseValue(final Parameter.Key parameterKey, final ArgumentReader.Mutable reader, final CommandContext.Builder context) throws ArgumentParseException { + return Optional.empty(); + } + + @Override + public ResourceKey key() { + return this.key; + } +} diff --git a/src/main/java/org/spongepowered/common/command/brigadier/argument/ComplexSuggestionNodeProvider.java b/src/main/java/org/spongepowered/common/command/brigadier/argument/ComplexSuggestionNodeProvider.java index 1b95a1ef1cf..9701e86ae54 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/argument/ComplexSuggestionNodeProvider.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/argument/ComplexSuggestionNodeProvider.java @@ -25,7 +25,7 @@ package org.spongepowered.common.command.brigadier.argument; import com.mojang.brigadier.tree.CommandNode; -import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.CommandSourceStack; import java.util.List; import java.util.function.Consumer; @@ -39,8 +39,8 @@ public interface ComplexSuggestionNodeProvider { /** * Creates the nodes. */ - CommandNode createSuggestions(final CommandNode rootNode, final String key, final boolean isTerminal, - final Consumer>> commandNodeListConsumer, - final Consumer> mapInsertionConsumer, boolean allowCustomSuggestionsOnTheFirstElement); + CommandNode createSuggestions(final CommandNode rootNode, final String key, final boolean isTerminal, + final Consumer>> commandNodeListConsumer, + final Consumer> mapInsertionConsumer, boolean allowCustomSuggestionsOnTheFirstElement); } diff --git a/src/main/java/org/spongepowered/common/command/brigadier/argument/StandardArgumentParser.java b/src/main/java/org/spongepowered/common/command/brigadier/argument/StandardArgumentParser.java index 92affbb8265..45666543f62 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/argument/StandardArgumentParser.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/argument/StandardArgumentParser.java @@ -41,6 +41,7 @@ import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.command.parameter.managed.ValueParameter; import org.spongepowered.api.command.parameter.managed.ValueParameterModifier; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.command.SpongeCommandCompletion; import org.spongepowered.common.command.brigadier.SpongeStringReader; @@ -144,6 +145,11 @@ public List complete(final @NonNull CommandCause context, fin } } + @Override + public StandardArgumentParser bind(final RegistryHolder registryHolder) { + return this; + } + @FunctionalInterface public interface Converter { Converter IDENTITY = (reader, cause, input) -> input; diff --git a/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/DelegatingCommandDispatcher.java b/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/DelegatingCommandDispatcher.java index 533fea294b4..bd90dad1bc1 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/DelegatingCommandDispatcher.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/DelegatingCommandDispatcher.java @@ -36,6 +36,8 @@ import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.RootCommandNode; import net.minecraft.commands.CommandSourceStack; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.common.command.registrar.BrigadierCommandRegistrar; import java.util.Collection; @@ -44,6 +46,7 @@ public final class DelegatingCommandDispatcher extends CommandDispatcher { private final BrigadierCommandRegistrar brigadier; + @MonotonicNonNull private PermissionService permissionService; public DelegatingCommandDispatcher(final BrigadierCommandRegistrar brigadier) { this.brigadier = brigadier; @@ -124,4 +127,12 @@ public CommandNode findNode(final Collection path) { public void findAmbiguities(final AmbiguityConsumer consumer) { this.brigadier.getDispatcher().findAmbiguities(consumer); } + + public void permissionService(final PermissionService permissionService) { + this.permissionService = permissionService; + } + + public PermissionService permissionService() { + return this.permissionService; + } } diff --git a/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/SpongeCommandDispatcher.java b/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/SpongeCommandDispatcher.java index dce0439d857..11830d960cb 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/SpongeCommandDispatcher.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/SpongeCommandDispatcher.java @@ -379,6 +379,10 @@ private ParseResults parseNodes0( try { child.parse(reader, context); } catch (final RuntimeException ex) { + if (!SpongeCommon.game().isServerAvailable()) { + // Log exception during startup + SpongeCommon.logger().error("Failed to parse command", ex); + } throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherParseException().createWithContext(reader, ex.getMessage()); } // Sponge Start: if we didn't consume anything we don't want Brig to complain at us. diff --git a/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/SpongeNodePermissionCache.java b/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/SpongeNodePermissionCache.java index 018e7377809..f85eae9e1b0 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/SpongeNodePermissionCache.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/dispatcher/SpongeNodePermissionCache.java @@ -27,7 +27,9 @@ import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.RootCommandNode; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.permissions.PermissionLevel; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.Sponge; import org.spongepowered.api.command.CommandCause; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.common.SpongeCommon; @@ -64,7 +66,7 @@ public static boolean canUse( && ((CommandSourceStackBridge) source).bridge$getCommandSource() instanceof ServerPlayer) { // If the entity is a player, then we should try to add it anyway. final String permission = supplier.get(); - SpongePermissions.registerPermission(permission, 0); + SpongePermissions.registerPermission(Sponge.server().serviceProvider().permissionService(), permission, PermissionLevel.ALL); return ((CommandCause) source).hasPermission(permission); } return result; diff --git a/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectArgumentSuggestionNode.java b/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectArgumentSuggestionNode.java index 1fdd097beda..96c28f88332 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectArgumentSuggestionNode.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectArgumentSuggestionNode.java @@ -29,29 +29,29 @@ import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.tree.ArgumentCommandNode; import com.mojang.brigadier.tree.CommandNode; -import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.CommandSourceStack; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Objects; -public final class ForcedRedirectArgumentSuggestionNode extends ArgumentCommandNode implements ForcedRedirectNode { +public final class ForcedRedirectArgumentSuggestionNode extends ArgumentCommandNode implements ForcedRedirectNode { - private @Nullable CommandNode forcedRedirect = null; + private @Nullable CommandNode forcedRedirect = null; public ForcedRedirectArgumentSuggestionNode(final String name, final ArgumentType type, - final Command command, - final SuggestionProvider customSuggestions) { + final Command command, + final SuggestionProvider customSuggestions) { super(name, type, command, isp -> true, null, null, false, customSuggestions); } @Override - public void setForcedRedirect(final @Nullable CommandNode redirect) { + public void setForcedRedirect(final @Nullable CommandNode redirect) { this.forcedRedirect = redirect; } @Override - public CommandNode getRedirect() { + public CommandNode getRedirect() { return this.forcedRedirect; } diff --git a/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectLiteralCommandNode.java b/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectLiteralCommandNode.java index 4dc8aca8025..28a7275c199 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectLiteralCommandNode.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectLiteralCommandNode.java @@ -26,25 +26,25 @@ import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; -import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.CommandSourceStack; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.command.registrar.tree.builder.AbstractCommandTreeNode; -public final class ForcedRedirectLiteralCommandNode extends LiteralCommandNode implements ForcedRedirectNode { +public final class ForcedRedirectLiteralCommandNode extends LiteralCommandNode implements ForcedRedirectNode { - private @Nullable CommandNode forcedRedirect = null; + private @Nullable CommandNode forcedRedirect = null; public ForcedRedirectLiteralCommandNode(final String literal, final boolean executable) { super(literal, executable ? AbstractCommandTreeNode.EXECUTABLE : null, c -> true, null, null, false); } @Override - public void setForcedRedirect(final CommandNode node) { + public void setForcedRedirect(final CommandNode node) { this.forcedRedirect = node; } @Override - public CommandNode getRedirect() { + public CommandNode getRedirect() { return this.forcedRedirect; } diff --git a/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectNode.java b/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectNode.java index 2b33dada2a0..4822fc2b895 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectNode.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/tree/ForcedRedirectNode.java @@ -25,9 +25,9 @@ package org.spongepowered.common.command.brigadier.tree; import com.mojang.brigadier.tree.CommandNode; -import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.CommandSourceStack; public interface ForcedRedirectNode { - void setForcedRedirect(CommandNode node); + void setForcedRedirect(CommandNode node); } diff --git a/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeArgumentCommandNode.java b/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeArgumentCommandNode.java index dcfb75114fc..8532aede6d4 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeArgumentCommandNode.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeArgumentCommandNode.java @@ -42,7 +42,6 @@ import com.mojang.brigadier.tree.ArgumentCommandNode; import com.mojang.brigadier.tree.CommandNode; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.synchronization.SuggestionProviders; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.command.CommandCompletion; @@ -144,10 +143,10 @@ public final boolean isComplex() { return this.isComplexSuggestions; } - public final CommandNode getComplexSuggestions( - final CommandNode rootSuggestionNode, - final Map, CommandNode> commandNodeToSuggestionNode, - final Map, List>> commandNodeListMap, + public final CommandNode getComplexSuggestions( + final CommandNode rootSuggestionNode, + final Map, CommandNode> commandNodeToSuggestionNode, + final Map, List>> commandNodeListMap, final boolean allowCustomSuggestionsOnTheFirstElement) { if (!this.isComplexSuggestions) { throw new IllegalStateException("The parser is not a ComplexSuggestionNodeParser"); @@ -176,17 +175,17 @@ private ArgumentType switchTypeIfRequired(final ArgumentType type) { } @SuppressWarnings({"unchecked", "rawtypes"}) - public final ArgumentBuilder createBuilderForSuggestions( - final CommandNode rootSuggestionNode, - final Map, CommandNode> commandNodeToSuggestionNode + public final ArgumentBuilder createBuilderForSuggestions( + final CommandNode rootSuggestionNode, + final Map, CommandNode> commandNodeToSuggestionNode ) { ArgumentType type = this.switchTypeIfRequired(this.getType()); - CommandNode previousNode = rootSuggestionNode; + CommandNode previousNode = rootSuggestionNode; if (!this.parser.getClientCompletionArgumentType().isEmpty()) { // create multiple entries, return the last one final boolean forceCustomSuggestions; final Collection> types = this.parser.getClientCompletionArgumentType().stream() - .filter(Objects::nonNull).collect(Collectors.toList()); + .filter(Objects::nonNull).toList(); if (types.size() > 1) { forceCustomSuggestions = false; // handled in here final Iterator> clientCompletionTypeIterator = this.parser.getClientCompletionArgumentType().iterator(); @@ -197,13 +196,13 @@ private ArgumentType switchTypeIfRequired(final ArgumentType type) { final boolean forceCustomSuggestionsInner = type != this.getType() && !CommandUtil.checkForCustomSuggestions(previousNode); if (clientCompletionTypeIterator.hasNext()) { // create node - final RequiredArgumentBuilder arg = RequiredArgumentBuilder.argument(this.getName(), type); + final RequiredArgumentBuilder arg = RequiredArgumentBuilder.argument(this.getName(), type); arg.requires(x -> true); // if the first node is forced to be custom suggestions or is a string argument type, send the completions. if (forceCustomSuggestionsInner || isFirst && type instanceof StringArgumentType) { - arg.suggests(SuggestionProviders.ASK_SERVER); + arg.suggests((SuggestionProvider) SuggestionProviders.ASK_SERVER); } - final CommandNode built = arg.build(); + final CommandNode built = arg.build(); previousNode.addChild(built); previousNode = built; if (isFirst) { @@ -218,17 +217,17 @@ private ArgumentType switchTypeIfRequired(final ArgumentType type) { forceCustomSuggestions = type != originalType; } - final RequiredArgumentBuilder toReturn = RequiredArgumentBuilder.argument(this.getUsageTextForClient(), type); + final RequiredArgumentBuilder toReturn = RequiredArgumentBuilder.argument(this.getUsageTextForClient(), type); if (this.getCommand() != null) { toReturn.executes(x -> 0); } if (this.modifier != null || forceCustomSuggestions && !CommandUtil.checkForCustomSuggestions(previousNode)) { - toReturn.suggests(SuggestionProviders.ASK_SERVER); + toReturn.suggests((SuggestionProvider) SuggestionProviders.ASK_SERVER); } else if (this.getCustomSuggestions() != null) { - toReturn.suggests((SuggestionProvider) this.getCustomSuggestions()); + toReturn.suggests(this.getCustomSuggestions()); } if (this.getRedirect() != null) { - toReturn.forward((CommandNode) this.getRedirect(), (RedirectModifier) this.getRedirectModifier(), this.isFork()); + toReturn.forward(this.getRedirect(), this.getRedirectModifier(), this.isFork()); } return toReturn; } @@ -249,7 +248,7 @@ private ArgumentType switchTypeIfRequired(final ArgumentType type) { if (this.getCommand() != null) { builder.executes(this.getCommand()); } - return (ArgumentBuilder) builder; + return builder; } public final ArgumentParser getParser() { diff --git a/src/main/java/org/spongepowered/common/command/brigadier/tree/SuggestionArgumentNode.java b/src/main/java/org/spongepowered/common/command/brigadier/tree/SuggestionArgumentNode.java index ec6ac5af671..1c1d25b56a6 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/tree/SuggestionArgumentNode.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/tree/SuggestionArgumentNode.java @@ -27,14 +27,14 @@ import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.tree.ArgumentCommandNode; import com.mojang.brigadier.tree.CommandNode; -import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.CommandSourceStack; import java.util.Objects; // Used to differentiate between custom suggestions -public final class SuggestionArgumentNode extends ArgumentCommandNode { +public final class SuggestionArgumentNode extends ArgumentCommandNode { - public SuggestionArgumentNode(final RequiredArgumentBuilder builder) { + public SuggestionArgumentNode(final RequiredArgumentBuilder builder) { super(builder.getName(), builder.getType(), builder.getCommand(), @@ -43,7 +43,7 @@ public SuggestionArgumentNode(final RequiredArgumentBuilder argument : builder.getArguments()) { + for (final CommandNode argument : builder.getArguments()) { this.addChild(argument); } } diff --git a/src/main/java/org/spongepowered/common/command/manager/SpongeCommandCauseFactory.java b/src/main/java/org/spongepowered/common/command/manager/SpongeCommandCauseFactory.java index a22773569cf..7e1ea641a09 100644 --- a/src/main/java/org/spongepowered/common/command/manager/SpongeCommandCauseFactory.java +++ b/src/main/java/org/spongepowered/common/command/manager/SpongeCommandCauseFactory.java @@ -28,6 +28,8 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.permissions.LevelBasedPermissionSet; +import net.minecraft.server.permissions.PermissionLevel; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; @@ -58,9 +60,9 @@ public final class SpongeCommandCauseFactory implements CommandCause.Factory { final CommandSource iCommandSource = cause.first(CommandSource.class).orElseGet(() -> SpongeCommon.game().systemSubject()); final CommandSourceStack commandSource; - if (iCommandSource instanceof CommandSourceProviderBridge) { + if (iCommandSource instanceof CommandSourceProviderBridge cspb) { // We know about this one so we can create it using the factory method on the source. - commandSource = ((CommandSourceProviderBridge) iCommandSource).bridge$getCommandSource(cause); + commandSource = cspb.bridge$getCommandSource(cause); } else { // try to create a command cause from the given ICommandSource, but as Mojang did not see fit to // put any identifying characteristics on the object, we have to go it alone... @@ -88,7 +90,7 @@ public final class SpongeCommandCauseFactory implements CommandCause.Factory { context.get(EventContextKeys.LOCATION).map(x -> (ServerLevel) x.world()) .orElseGet(() -> locatable == null ? SpongeCommon.server().getLevel(Level.OVERWORLD) : (ServerLevel) locatable.serverLocation().world()), - 4, + LevelBasedPermissionSet.forLevel(PermissionLevel.OWNERS), name, displayName, SpongeCommon.server(), diff --git a/src/main/java/org/spongepowered/common/command/manager/SpongeCommandManager.java b/src/main/java/org/spongepowered/common/command/manager/SpongeCommandManager.java index 1602e9dbffc..f38aed449ed 100644 --- a/src/main/java/org/spongepowered/common/command/manager/SpongeCommandManager.java +++ b/src/main/java/org/spongepowered/common/command/manager/SpongeCommandManager.java @@ -46,7 +46,6 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.util.ComponentMessageThrowable; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.server.MinecraftServer; import org.apache.logging.log4j.Level; import org.checkerframework.checker.nullness.qual.NonNull; @@ -62,13 +61,13 @@ import org.spongepowered.api.command.manager.CommandManager; import org.spongepowered.api.command.manager.CommandMapping; import org.spongepowered.api.command.registrar.CommandRegistrar; -import org.spongepowered.api.command.registrar.CommandRegistrarType; import org.spongepowered.api.command.registrar.tree.CommandTreeNode; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.service.pagination.PaginationService; import org.spongepowered.api.service.permission.Subject; @@ -437,18 +436,17 @@ private void prettyPrintThrowableError(final Throwable thr, final String command } } - public void init() { + public void init(final RegistryHolder registryHolder) { final Cause cause = PhaseTracker.getInstance().currentCause(); final Set> usedTokens = new HashSet<>(); - Sponge.game().registry(RegistryTypes.COMMAND_REGISTRAR_TYPE).streamEntries().forEach(entry -> { - final CommandRegistrarType type = entry.value(); + registryHolder.registry(RegistryTypes.COMMAND_REGISTRAR_TYPE).stream().forEach(type -> { // someone's gonna do it, let's not let them take us down. final TypeToken handledType = type.handledType(); if (handledType == null) { SpongeCommon.logger().error("Registrar '{}' did not provide a handledType, skipping...", type.getClass()); } else if (usedTokens.add(handledType)) { // we haven't done it yet // Add the command registrar - final CommandRegistrar registrar = type.create(this); + final CommandRegistrar registrar = type.create(this, registryHolder); this.knownRegistrars.put(GenericTypeReflector.erase(type.handledType().getType()), registrar); if (registrar instanceof BrigadierCommandRegistrar) { this.brigadierRegistrar = (BrigadierCommandRegistrar) registrar; @@ -456,7 +454,7 @@ public void init() { this.registerInternalCommands((SpongeParameterizedCommandRegistrar) registrar); } - this.game.eventManager().post(this.createEvent(cause, this.game, registrar)); + this.game.eventManager().post(this.createEvent(cause, this.game, registrar, registryHolder)); } else { SpongeCommon.logger() .warn("Command type '{}' has already been collected, skipping request from {}", @@ -498,22 +496,22 @@ private void registerInternalCommands(final CommandRegistrar regi CallbackCommand.NAME); } - public Collection> getNonBrigadierSuggestions(final CommandCause cause) { - final List> suggestions = new ArrayList<>(); + public Collection> getNonBrigadierSuggestions(final CommandCause cause) { + final List> suggestions = new ArrayList<>(); for (final Map.Entry entry : this.mappingToSuggestionNodes.entrySet()) { final SpongeCommandMapping mapping = entry.getKey(); // create tree from primary mapping - final CommandNode node = entry.getValue() + final CommandNode node = entry.getValue() .createArgumentTree(cause, LiteralArgumentBuilder.literal(mapping.primaryAlias())); if (node != null) { - final Command executableCommand = node.getCommand(); - final CommandNode toRedirectTo = node.getRedirect() == null ? node : node.getRedirect(); + final Command executableCommand = node.getCommand(); + final CommandNode toRedirectTo = node.getRedirect() == null ? node : node.getRedirect(); suggestions.add(node); for (final String alias : mapping.allAliases()) { if (!alias.equals(mapping.primaryAlias())) { - suggestions.add(LiteralArgumentBuilder.literal(alias) + suggestions.add(LiteralArgumentBuilder.literal(alias) .executes(executableCommand).redirect(toRedirectTo).build()); } } @@ -528,7 +526,7 @@ public Collection getAliasesThatStartWithForCause(final CommandCause cau final Object2BooleanMap testedMappings = new Object2BooleanOpenHashMap<>(); for (final Map.Entry mappingEntry : this.commandMappings.entrySet()) { if (mappingEntry.getKey().startsWith(toCompare)) { - if (testedMappings.computeBooleanIfAbsent(mappingEntry.getValue(), mapping -> mapping.registrar().canExecute(cause, mapping))) { + if (testedMappings.computeIfAbsent(mappingEntry.getValue(), (CommandMapping mapping) -> mapping.registrar().canExecute(cause, mapping))) { aliases.add(toCompare); } } @@ -546,11 +544,12 @@ public Collection getAliasesForCause(final CommandCause cause) { return aliases; } - private > RegisterCommandEventImpl createEvent(final Cause cause, final Game game, final R registrar) { + private > RegisterCommandEventImpl createEvent(final Cause cause, final Game game, final R registrar, final RegistryHolder registryHolder) { return new RegisterCommandEventImpl<>( cause, game, - registrar + registrar, + registryHolder ); } diff --git a/src/main/java/org/spongepowered/common/command/parameter/SpongeParameterValue.java b/src/main/java/org/spongepowered/common/command/parameter/SpongeParameterValue.java index b6a38e5b994..2020f381d98 100644 --- a/src/main/java/org/spongepowered/common/command/parameter/SpongeParameterValue.java +++ b/src/main/java/org/spongepowered/common/command/parameter/SpongeParameterValue.java @@ -134,7 +134,7 @@ private void parseInternal( // If we get this far, we failed to parse, return the exceptions if (currentExceptions == null) { - final var component = Component.translatable("command.exception").args(Component.text("Could not parse element")); + final var component = Component.translatable("command.exception").arguments(Component.text("Could not parse element")); throw new ArgumentParseException(component, args.input(), args.cursor()); } else if (currentExceptions.size() == 1) { throw currentExceptions.get(0); diff --git a/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeColorValueParameter.java b/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeColorValueParameter.java index 7a4fb9c3001..227c2497441 100644 --- a/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeColorValueParameter.java +++ b/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeColorValueParameter.java @@ -30,8 +30,8 @@ import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import net.kyori.adventure.text.Component; -import net.minecraft.ChatFormatting; -import net.minecraft.commands.arguments.ColorArgument; +import net.minecraft.commands.arguments.TeamColorArgument; +import net.minecraft.world.scores.TeamColor; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.command.CommandCause; @@ -39,12 +39,10 @@ import org.spongepowered.api.command.exception.ArgumentParseException; import org.spongepowered.api.command.parameter.ArgumentReader; import org.spongepowered.api.util.Color; -import org.spongepowered.common.accessor.ChatFormattingAccessor; import org.spongepowered.common.command.SpongeCommandCompletion; import org.spongepowered.common.command.brigadier.argument.ResourceKeyedArgumentValueParser; import org.spongepowered.common.util.Constants; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -67,11 +65,10 @@ public final class SpongeColorValueParameter extends ResourceKeyedArgumentValueP .append(Component.text("* Comma separated RGB color, surrounded by double quotes, with values from 0 to 255 (such as \"0,128,255\")")) .build(); private final static Pattern HEX_CODE = Pattern.compile("(#?)(?[0-9A-Fa-f]{6})"); - private final static Collection CHAT_FORMATTING_NAMES = Arrays.stream(ChatFormatting.values()) - .filter(x -> x.isColor()) - .map(x -> x.getName().toLowerCase(Locale.ROOT)) - .collect(Collectors.toList()); - private final ColorArgument colorArgumentType = ColorArgument.color(); + private final static Collection TEAM_COLOR_NAMES = TeamColor.VALUES.stream() + .map(TeamColor::getSerializedName) + .toList(); + private final TeamColorArgument colorArgumentType = TeamColorArgument.teamColor(); public SpongeColorValueParameter(final ResourceKey key) { super(key); @@ -92,13 +89,8 @@ public CompletableFuture listSuggestions( final ArgumentReader.Immutable state = reader.immutable(); // First, is the argument type giving the correct return type? try { - final ChatFormatting formatting = this.colorArgumentType.parse((StringReader) reader); - final Integer colorCode = ((ChatFormattingAccessor) (Object) formatting).accessor$color(); - if (colorCode != null) { - return Optional.of(Color.ofRgb(colorCode)); - } - // "reset" slips through the net here. - throw reader.createException(Component.text().content(String.format("%s is not a valid color", formatting.getName())).build()); + final TeamColor parsed = this.colorArgumentType.parse((StringReader) reader); + return Optional.of(Color.ofRgb(parsed.rgb())); } catch (final CommandSyntaxException e) { // ignored } @@ -130,7 +122,7 @@ public CompletableFuture listSuggestions( @Override public List complete(final CommandCause context, final String currentInput) { - return SpongeColorValueParameter.CHAT_FORMATTING_NAMES + return SpongeColorValueParameter.TEAM_COLOR_NAMES .stream() .filter(x -> x.startsWith(currentInput.toLowerCase(Locale.ROOT))) .map(SpongeCommandCompletion::new) diff --git a/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeGameProfileValueParameter.java b/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeGameProfileValueParameter.java index 3f770dd39e5..7a58ebe1168 100644 --- a/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeGameProfileValueParameter.java +++ b/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeGameProfileValueParameter.java @@ -31,6 +31,7 @@ import net.kyori.adventure.text.Component; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.arguments.GameProfileArgument; +import net.minecraft.server.players.NameAndId; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.command.CommandCause; @@ -64,7 +65,7 @@ public CompletableFuture listSuggestions( public Optional parseValue( final @NonNull CommandCause cause, final ArgumentReader.@NonNull Mutable reader) throws ArgumentParseException { try { - final Collection profileCollection = + final Collection profileCollection = this.argument.parse((StringReader) reader).getNames((CommandSourceStack) cause); if (profileCollection.size() == 1) { return Optional.of(SpongeGameProfile.of(profileCollection.iterator().next())); diff --git a/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeServerLocationValueParameter.java b/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeServerLocationValueParameter.java index b95d8e5ef7c..06cd38eec82 100644 --- a/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeServerLocationValueParameter.java +++ b/src/main/java/org/spongepowered/common/command/parameter/managed/standard/SpongeServerLocationValueParameter.java @@ -32,10 +32,9 @@ import com.mojang.brigadier.tree.CommandNode; import net.kyori.adventure.text.Component; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.arguments.coordinates.Coordinates; import net.minecraft.commands.arguments.coordinates.Vec3Argument; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.phys.Vec3; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.ResourceKey; @@ -88,11 +87,11 @@ private Stream complete(final String currentInput) { final ArgumentReader.Immutable state = reader.immutable(); ServerWorld serverWorld; try { - final ResourceKey resourceLocation = reader.parseResourceKey("minecraft"); + final ResourceKey Identifier = reader.parseResourceKey("minecraft"); serverWorld = SpongeCommon.game().server().worldManager() - .world(resourceLocation) + .world(Identifier) .orElseThrow(() -> reader.createException( - Component.text("Could not get world with key \"" + resourceLocation.toString() + "\""))); + Component.text("Could not get world with key \"" + Identifier + "\""))); } catch (final ArgumentParseException e) { final Optional location = cause.location(); if (location.isPresent()) { @@ -122,13 +121,13 @@ public List> getClientCompletionArgumentType() { } @Override - public CommandNode createSuggestions(final CommandNode rootNode, final String key, + public CommandNode createSuggestions(final CommandNode rootNode, final String key, final boolean isTerminal, - final Consumer>> firstNodes, - final Consumer> redirectionNodes, + final Consumer>> firstNodes, + final Consumer> redirectionNodes, final boolean allowCustomSuggestionsOnTheFirstElement) { - final RequiredArgumentBuilder firstNode = + final RequiredArgumentBuilder firstNode = RequiredArgumentBuilder.argument(key, Constants.Command.RESOURCE_LOCATION_TYPE); if (allowCustomSuggestionsOnTheFirstElement) { firstNode.suggests((context, builder) -> { @@ -136,18 +135,18 @@ public CommandNode createSuggestions(final CommandNode return builder.buildFuture(); }); } - final RequiredArgumentBuilder secondNode = + final RequiredArgumentBuilder secondNode = RequiredArgumentBuilder.argument(key + "_pos", Vec3Argument.vec3()); if (isTerminal) { secondNode.executes(x -> 1); } - final CommandNode second = secondNode.build(); + final CommandNode second = secondNode.build(); firstNode.then(second); - final CommandNode first = firstNode.build(); + final CommandNode first = firstNode.build(); redirectionNodes.accept(second); rootNode.addChild(first); rootNode.addChild(second); - final List> nodesToAttach = new ArrayList<>(); + final List> nodesToAttach = new ArrayList<>(); nodesToAttach.add(first); nodesToAttach.add(second); firstNodes.accept(nodesToAttach); diff --git a/src/main/java/org/spongepowered/common/command/registrar/BrigadierCommandRegistrar.java b/src/main/java/org/spongepowered/common/command/registrar/BrigadierCommandRegistrar.java index baa3d25da00..da0311d7094 100644 --- a/src/main/java/org/spongepowered/common/command/registrar/BrigadierCommandRegistrar.java +++ b/src/main/java/org/spongepowered/common/command/registrar/BrigadierCommandRegistrar.java @@ -44,6 +44,7 @@ import org.spongepowered.api.command.manager.CommandMapping; import org.spongepowered.api.command.registrar.CommandRegistrar; import org.spongepowered.api.command.registrar.CommandRegistrarType; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.util.Tuple; import org.spongepowered.common.command.SpongeCommandCompletion; import org.spongepowered.common.command.brigadier.dispatcher.SpongeCommandDispatcher; @@ -76,9 +77,9 @@ public final class BrigadierCommandRegistrar implements BrigadierBasedRegistrar< ); private final SpongeCommandManager manager; - private SpongeCommandDispatcher dispatcher; + private final SpongeCommandDispatcher dispatcher; - public BrigadierCommandRegistrar(final CommandManager.Mutable manager) { + public BrigadierCommandRegistrar(final CommandManager.Mutable manager, final RegistryHolder registryHolder) { this.manager = (SpongeCommandManager) manager; this.dispatcher = new SpongeCommandDispatcher(this.manager); } diff --git a/src/main/java/org/spongepowered/common/command/registrar/SpongeCommandRegistrarType.java b/src/main/java/org/spongepowered/common/command/registrar/SpongeCommandRegistrarType.java index e0ce31c54d1..3fc94920442 100644 --- a/src/main/java/org/spongepowered/common/command/registrar/SpongeCommandRegistrarType.java +++ b/src/main/java/org/spongepowered/common/command/registrar/SpongeCommandRegistrarType.java @@ -29,16 +29,17 @@ import org.spongepowered.api.command.manager.CommandManager; import org.spongepowered.api.command.registrar.CommandRegistrar; import org.spongepowered.api.command.registrar.CommandRegistrarType; +import org.spongepowered.api.registry.RegistryHolder; -import java.util.function.Function; +import java.util.function.BiFunction; public final class SpongeCommandRegistrarType implements CommandRegistrarType { private final TypeToken handledType; - private final Function> supplier; + private final BiFunction> supplier; public SpongeCommandRegistrarType( final TypeToken handledType, - final Function> supplier + final BiFunction> supplier ) { this.handledType = handledType; this.supplier = supplier; @@ -46,7 +47,7 @@ public SpongeCommandRegistrarType( public SpongeCommandRegistrarType( final Class handledType, - final Function> supplier + final BiFunction> supplier ) { this(TypeToken.get(handledType), supplier); } @@ -57,7 +58,7 @@ public SpongeCommandRegistrarType( } @Override - public @NonNull CommandRegistrar create(final CommandManager.@NonNull Mutable manager) { - return this.supplier.apply(manager); + public @NonNull CommandRegistrar create(final CommandManager.@NonNull Mutable manager, final RegistryHolder registryHolder) { + return this.supplier.apply(manager, registryHolder); } } diff --git a/src/main/java/org/spongepowered/common/command/registrar/SpongeParameterizedCommandRegistrar.java b/src/main/java/org/spongepowered/common/command/registrar/SpongeParameterizedCommandRegistrar.java index dc02fbff51a..05b612d8f3d 100644 --- a/src/main/java/org/spongepowered/common/command/registrar/SpongeParameterizedCommandRegistrar.java +++ b/src/main/java/org/spongepowered/common/command/registrar/SpongeParameterizedCommandRegistrar.java @@ -39,6 +39,7 @@ import org.spongepowered.api.command.manager.CommandManager; import org.spongepowered.api.command.manager.CommandMapping; import org.spongepowered.api.command.registrar.CommandRegistrarType; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.common.command.SpongeCommandCompletion; import org.spongepowered.common.command.SpongeParameterizedCommand; import org.spongepowered.common.command.brigadier.dispatcher.SpongeCommandDispatcher; @@ -66,10 +67,12 @@ public final class SpongeParameterizedCommandRegistrar implements BrigadierBased ); private final CommandManager.Mutable commandManager; + private final RegistryHolder registryHolder; private final Map commandMap = new HashMap<>(); - public SpongeParameterizedCommandRegistrar(final CommandManager.Mutable commandManager) { + public SpongeParameterizedCommandRegistrar(final CommandManager.Mutable commandManager, final RegistryHolder registryHolder) { this.commandManager = commandManager; + this.registryHolder = registryHolder; } @Override @@ -102,8 +105,8 @@ private SpongeCommandManager commandManager() { null ); - this.createNode(mapping, command).forEach(this.commandManager().getDispatcher()::register); - ((SpongeParameterizedCommand) command).setCommandManager(this.commandManager()); + this.createNode(this.registryHolder, mapping, command).forEach(this.commandManager().getDispatcher()::register); + ((SpongeParameterizedCommand) command).setCommandManager(this.commandManager(), this.registryHolder); this.commandMap.put(mapping, command); return mapping; } @@ -194,11 +197,11 @@ private String createCommandString(final String command, final String argument) return command + " " + argument; } - private Collection> createNode(final CommandMapping mapping, final Command.Parameterized command) { + private Collection> createNode(final RegistryHolder registryHolder, final CommandMapping mapping, final Command.Parameterized command) { if (!(command instanceof SpongeParameterizedCommand)) { throw new IllegalArgumentException("Command must be a SpongeParameterizedCommand!"); } - return ((SpongeParameterizedCommand) command).buildWithAliases(mapping.allAliases()); + return ((SpongeParameterizedCommand) command).buildWithAliases(registryHolder, mapping.allAliases()); } } diff --git a/src/main/java/org/spongepowered/common/command/registrar/SpongeRawCommandRegistrar.java b/src/main/java/org/spongepowered/common/command/registrar/SpongeRawCommandRegistrar.java index 5102354ecc5..698a28c3b9b 100644 --- a/src/main/java/org/spongepowered/common/command/registrar/SpongeRawCommandRegistrar.java +++ b/src/main/java/org/spongepowered/common/command/registrar/SpongeRawCommandRegistrar.java @@ -37,6 +37,7 @@ import org.spongepowered.api.command.manager.CommandMapping; import org.spongepowered.api.command.registrar.CommandRegistrar; import org.spongepowered.api.command.registrar.CommandRegistrarType; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.common.command.brigadier.SpongeStringReader; import org.spongepowered.plugin.PluginContainer; @@ -55,9 +56,11 @@ public final class SpongeRawCommandRegistrar implements CommandRegistrar commands = new HashMap<>(); private final CommandManager.Mutable manager; + private final RegistryHolder registryHolder; - SpongeRawCommandRegistrar(final CommandManager.Mutable manager) { + SpongeRawCommandRegistrar(final CommandManager.Mutable manager, final RegistryHolder registryHolder) { this.manager = manager; + this.registryHolder = registryHolder; } @Override @@ -75,7 +78,7 @@ public CommandMapping register( final CommandMapping mapping = this.manager.registerAlias( this, container, - command.commandTree(), + command.commandTree(this.registryHolder), primaryAlias, secondaryAliases ); diff --git a/src/main/java/org/spongepowered/common/command/registrar/tree/builder/AbstractCommandTreeNode.java b/src/main/java/org/spongepowered/common/command/registrar/tree/builder/AbstractCommandTreeNode.java index bf04f67d1c2..6679e8a3aa6 100644 --- a/src/main/java/org/spongepowered/common/command/registrar/tree/builder/AbstractCommandTreeNode.java +++ b/src/main/java/org/spongepowered/common/command/registrar/tree/builder/AbstractCommandTreeNode.java @@ -28,7 +28,7 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.tree.CommandNode; -import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.synchronization.SuggestionProviders; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -42,10 +42,10 @@ import java.util.Objects; import java.util.function.Predicate; -public abstract class AbstractCommandTreeNode, O extends CommandNode> +public abstract class AbstractCommandTreeNode, O extends CommandNode> implements CommandTreeNode<@NonNull T> { - public final static Command EXECUTABLE = isp -> 1; + public final static Command EXECUTABLE = isp -> 1; private @Nullable CommandTreeNode redirect = null; private @Nullable Map> children = null; @@ -125,8 +125,8 @@ public boolean isExecutable() { } @SuppressWarnings("unchecked") - public SuggestionProvider suggestionProvider() { - return (SuggestionProvider) this.completionProvider; + public SuggestionProvider suggestionProvider() { + return (SuggestionProvider) this.completionProvider; } public @Nullable CommandTreeNode<@NonNull ?> getRedirect() { @@ -137,14 +137,14 @@ public SuggestionProvider suggestionProvider() { protected final void addChildNodesToTree( final CommandCause cause, - final CommandNode node, - final Map, CommandNode> nodeToSuggestionProvider, + final CommandNode node, + final Map, CommandNode> nodeToSuggestionProvider, final Map> redirectsToApply) { this.getChildren().forEach((key, value) -> { if (value.requirement.test(cause)) { - final CommandNode providerCommandNode = + final CommandNode providerCommandNode = nodeToSuggestionProvider.computeIfAbsent(value, k -> { - final CommandNode ret = k.createElement(key); + final CommandNode ret = k.createElement(key); if (k.redirect instanceof AbstractCommandTreeNode && ret instanceof ForcedRedirectNode) { redirectsToApply.put((ForcedRedirectNode) ret, (AbstractCommandTreeNode) k.redirect); } else { diff --git a/src/main/java/org/spongepowered/common/command/registrar/tree/builder/ArgumentCommandTreeNode.java b/src/main/java/org/spongepowered/common/command/registrar/tree/builder/ArgumentCommandTreeNode.java index 5f6bb1bb49f..894bdfd5504 100644 --- a/src/main/java/org/spongepowered/common/command/registrar/tree/builder/ArgumentCommandTreeNode.java +++ b/src/main/java/org/spongepowered/common/command/registrar/tree/builder/ArgumentCommandTreeNode.java @@ -26,13 +26,13 @@ import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.tree.CommandNode; -import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.CommandSourceStack; import org.spongepowered.api.command.registrar.tree.CommandTreeNode; import org.spongepowered.api.command.registrar.tree.CommandTreeNodeType; import org.spongepowered.common.command.brigadier.tree.ForcedRedirectArgumentSuggestionNode; public abstract class ArgumentCommandTreeNode> - extends AbstractCommandTreeNode> { + extends AbstractCommandTreeNode> { private final CommandTreeNodeType parameterType; @@ -41,7 +41,7 @@ public ArgumentCommandTreeNode(final CommandTreeNodeType parameterType) { } @Override - protected CommandNode createElement(final String nodeKey) { + protected CommandNode createElement(final String nodeKey) { return new ForcedRedirectArgumentSuggestionNode<>( nodeKey, this.getArgumentType(), diff --git a/src/main/java/org/spongepowered/common/command/registrar/tree/builder/LiteralCommandTreeNode.java b/src/main/java/org/spongepowered/common/command/registrar/tree/builder/LiteralCommandTreeNode.java index fb51e482799..aa6a2098464 100644 --- a/src/main/java/org/spongepowered/common/command/registrar/tree/builder/LiteralCommandTreeNode.java +++ b/src/main/java/org/spongepowered/common/command/registrar/tree/builder/LiteralCommandTreeNode.java @@ -25,15 +25,15 @@ package org.spongepowered.common.command.registrar.tree.builder; import com.mojang.brigadier.tree.LiteralCommandNode; -import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.CommandSourceStack; import org.spongepowered.api.command.registrar.tree.CommandTreeNode; import org.spongepowered.common.command.brigadier.tree.ForcedRedirectLiteralCommandNode; -public final class LiteralCommandTreeNode extends AbstractCommandTreeNode> +public final class LiteralCommandTreeNode extends AbstractCommandTreeNode> implements CommandTreeNode.Basic { @Override - protected LiteralCommandNode createElement(final String nodeKey) { + protected LiteralCommandNode createElement(final String nodeKey) { return new ForcedRedirectLiteralCommandNode( nodeKey, this.isExecutable() diff --git a/src/main/java/org/spongepowered/common/command/registrar/tree/builder/RootCommandTreeNode.java b/src/main/java/org/spongepowered/common/command/registrar/tree/builder/RootCommandTreeNode.java index 215d2eeaa53..398d74796fd 100644 --- a/src/main/java/org/spongepowered/common/command/registrar/tree/builder/RootCommandTreeNode.java +++ b/src/main/java/org/spongepowered/common/command/registrar/tree/builder/RootCommandTreeNode.java @@ -28,7 +28,7 @@ import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.RootCommandNode; -import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.CommandSourceStack; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.command.CommandCause; import org.spongepowered.api.command.registrar.tree.CommandTreeNode; @@ -38,12 +38,12 @@ import java.util.LinkedHashMap; import java.util.Map; -public final class RootCommandTreeNode extends AbstractCommandTreeNode> +public final class RootCommandTreeNode extends AbstractCommandTreeNode> implements CommandTreeNode.Root { - public @Nullable CommandNode createArgumentTree(final CommandCause cause, final LiteralArgumentBuilder rootBuilder) { + public @Nullable CommandNode createArgumentTree(final CommandCause cause, final LiteralArgumentBuilder rootBuilder) { if (this.getRequirement().test(cause)) { - final Map, CommandNode> nodeToSuggestionProvider = new IdentityHashMap<>(); + final Map, CommandNode> nodeToSuggestionProvider = new IdentityHashMap<>(); // this is going to be iterated only. final Map> redirectsToApply = new LinkedHashMap<>(); @@ -51,7 +51,7 @@ public final class RootCommandTreeNode extends AbstractCommandTreeNode 1); } - final LiteralCommandNode rootNode = rootBuilder.build(); + final LiteralCommandNode rootNode = rootBuilder.build(); this.addChildNodesToTree( cause, rootNode, @@ -67,7 +67,7 @@ public final class RootCommandTreeNode extends AbstractCommandTreeNode createElement(final String nodeKey) { + protected RootCommandNode createElement(final String nodeKey) { // node key is ignored. return new RootCommandNode<>(); } diff --git a/src/main/java/org/spongepowered/common/command/resolver/SpongeSuggestionTreeResolver.java b/src/main/java/org/spongepowered/common/command/resolver/SpongeSuggestionTreeResolver.java new file mode 100644 index 00000000000..585e52eef63 --- /dev/null +++ b/src/main/java/org/spongepowered/common/command/resolver/SpongeSuggestionTreeResolver.java @@ -0,0 +1,178 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.command.resolver; + +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.tree.ArgumentCommandNode; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.RootCommandNode; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.synchronization.SuggestionProviders; +import org.spongepowered.common.bridge.commands.arguments.CompletionsArgumentTypeBridge; +import org.spongepowered.common.command.brigadier.dispatcher.SpongeNodePermissionCache; +import org.spongepowered.common.command.brigadier.tree.SpongeArgumentCommandNode; +import org.spongepowered.common.command.brigadier.tree.SpongeNode; +import org.spongepowered.common.command.brigadier.tree.SuggestionArgumentNode; +import org.spongepowered.common.command.manager.SpongeCommandManager; +import org.spongepowered.common.util.CommandUtil; + +import java.util.*; + +public record SpongeSuggestionTreeResolver( + CommandSourceStack source, + Map, CommandNode> commandToSuggestion, + Map, List>> playerNodes, + SpongeCommandManager commandManager +) { + + /** + * Replacement of Commands#fillUsableCommands. + * There is no need to split this into injections to be compatible with mods because modded platforms never call fillUsableCommands. + * Instead, they redirect fillUsableCommands to their own method, like we do. + */ + public void fillSuggestions(final CommandNode rootCommandNode, final CommandNode rootSuggestion) { + for (final CommandNode commandNode : SpongeSuggestionTreeResolver.getChildren(rootCommandNode)) { + final List> existingNodes = this.playerNodes.get(commandNode); + if (existingNodes != null) { + if (!existingNodes.isEmpty()) { + boolean hasCustomSuggestionsAlready = CommandUtil.checkForCustomSuggestions(rootSuggestion); + for (final CommandNode node : existingNodes) { + // If we have custom suggestions, we need to limit it to one node, otherwise we trigger a bug + // in the client where it'll send more than one custom suggestion request - which is fine, except + // the client will then ignore all but one of them. This is a problem because we then end up with + // no suggestions - CompletableFuture.allOf(...) will contain an exception if a future is cancelled, + // meaning thenRun(...) does not run, which is how displaying the suggestions works... + // + // Because we don't control the client, we have to work around it here. + if (hasCustomSuggestionsAlready && node instanceof ArgumentCommandNode argNode) { + if (argNode.getCustomSuggestions() != null) { + // Rebuild the node without the custom suggestions. + rootSuggestion.addChild(SpongeSuggestionTreeResolver.cloneArgumentWithoutSuggestions(argNode)); + continue; + } + } else if (node instanceof ArgumentCommandNode && ((ArgumentCommandNode) node).getCustomSuggestions() != null) { + hasCustomSuggestionsAlready = true; // no more custom suggestions + } + rootSuggestion.addChild(node); + } + } + // If empty, we have a node that won't resolve (even if not complex), so we ignore it. + continue; + // If we have already processed this node, and it appears in the suggestion node list, prevent a potentially costly + // canUse check as we know we can already use it. + } + + if (!this.commandToSuggestion.containsKey(commandNode) && !SpongeNodePermissionCache.canUse( + rootCommandNode instanceof RootCommandNode, this.commandManager.getDispatcher(), commandNode, this.source)) { + this.playerNodes.put(commandNode, Collections.emptyList()); + continue; + } + + if (commandNode instanceof SpongeArgumentCommandNode spongeCommandNode && spongeCommandNode.isComplex()) { + final boolean hasCustomSuggestionsAlready = CommandUtil.checkForCustomSuggestions(rootSuggestion); + final CommandNode finalCommandNode = spongeCommandNode.getComplexSuggestions(rootSuggestion, this.commandToSuggestion, this.playerNodes, !hasCustomSuggestionsAlready); + if (!SpongeSuggestionTreeResolver.getChildren(commandNode).isEmpty()) { + this.fillSuggestions(commandNode, finalCommandNode); + } + continue; + } + + ArgumentBuilder builder; + if (commandNode instanceof SpongeArgumentCommandNode node) { + builder = node.createBuilderForSuggestions(rootSuggestion, this.commandToSuggestion); + } else if (commandNode instanceof ArgumentCommandNode argNode && argNode.getType() instanceof CompletionsArgumentTypeBridge argType) { + final RequiredArgumentBuilder r = RequiredArgumentBuilder.argument(argNode.getName(), argType.bridge$clientSideCompletionType()); + r.executes(argNode.getCommand()) + .forward(argNode.getRedirect(), argNode.getRedirectModifier(), argNode.isFork()) + .requires(argNode.getRequirement()); + if (!CommandUtil.checkForCustomSuggestions(rootSuggestion)) { + r.suggests((SuggestionProvider) SuggestionProviders.ASK_SERVER); + } + builder = r; + } else { + builder = commandNode.createBuilder(); + } + + // From above. + // + // If we have custom suggestions, we need to limit it to one node, otherwise we trigger a bug + // in the client where it'll send more than one custom suggestion request - which is fine, except + // the client will then ignore all but one of them. This is a problem because we then end up with + // no suggestions - CompletableFuture.allOf(...) will contain an exception if a future is cancelled, + // meaning thenRun(...) does not run, which is how displaying the suggestions works... + // + // Because we don't control the client, we have to work around it here. + if (builder instanceof RequiredArgumentBuilder r && r.getSuggestionsProvider() == SuggestionProviders.ASK_SERVER && CommandUtil.checkForCustomSuggestions(rootSuggestion)) { + r.suggests(null); + } + + if (builder.getRedirect() != null) { + builder.redirect(this.commandToSuggestion.get(builder.getRedirect())); + } + + final CommandNode suggestion = builder instanceof RequiredArgumentBuilder r ? new SuggestionArgumentNode(r) : builder.build(); + + if (!this.commandToSuggestion.containsKey(commandNode)) { + // done here because this check is applicable + if (!this.playerNodes.containsKey(commandNode)) { + final List> children = new ArrayList<>(); + children.add(suggestion); + this.playerNodes.put(commandNode, children); + } + + // If the current root suggestion has already got a custom suggestion and this node has a custom suggestion, + // we need to swap it out. + if (commandNode instanceof ArgumentCommandNode argNode && CommandUtil.checkForCustomSuggestions(rootSuggestion)) { + rootSuggestion.addChild(SpongeSuggestionTreeResolver.cloneArgumentWithoutSuggestions(argNode)); + } else { + rootSuggestion.addChild(suggestion); + } + this.commandToSuggestion.put(commandNode, suggestion); + } + + if (!commandNode.getChildren().isEmpty()) { + this.fillSuggestions(commandNode, suggestion); + } + } + } + + private static Collection> getChildren(final CommandNode node) { + if (node instanceof SpongeNode) { + return ((SpongeNode) node).getChildrenForSuggestions(); + } + return node.getChildren(); + } + + private static ArgumentCommandNode cloneArgumentWithoutSuggestions(final ArgumentCommandNode toClone) { + final RequiredArgumentBuilder builder = toClone.createBuilder(); + builder.suggests(null); + for (final CommandNode node : toClone.getChildren()) { + builder.then(node); + } + return new SuggestionArgumentNode<>(builder); + } +} diff --git a/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java b/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java index 54082cae6a9..5a89efe23ce 100644 --- a/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java +++ b/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java @@ -72,7 +72,7 @@ import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.plugin.metadata.model.PluginContributor; -import java.net.URL; +import java.net.URI; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collection; @@ -657,7 +657,7 @@ private void appendPluginMeta(final TextComponent.Builder builder, final String this.appendPluginMeta(builder, key, Component.text(value)); } - private void appendPluginMeta(final TextComponent.Builder builder, final String key, final URL value) { + private void appendPluginMeta(final TextComponent.Builder builder, final String key, final URI value) { final String url = value.toString(); this.appendPluginMeta(builder, key, Component.text().content(url).clickEvent(ClickEvent.openUrl(url)) .decoration(TextDecoration.UNDERLINED, true).build()); @@ -691,8 +691,6 @@ private Component createContainerMeta(final PluginMetadata pluginMetadata) { contributor.description().ifPresent(x -> builder.append(Component.text(" (" + x + ")"))); } } - - this.appendPluginMeta(builder, "Entrypoint", pluginMetadata.entrypoint()); return builder.build(); } diff --git a/src/main/java/org/spongepowered/common/config/DataTranslatableTypeSerializer.java b/src/main/java/org/spongepowered/common/config/DataTranslatableTypeSerializer.java new file mode 100644 index 00000000000..9ff9379395f --- /dev/null +++ b/src/main/java/org/spongepowered/common/config/DataTranslatableTypeSerializer.java @@ -0,0 +1,73 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.config; + +import io.leangen.geantyref.GenericTypeReflector; +import org.spongepowered.api.data.DataManager; +import org.spongepowered.api.data.persistence.DataTranslator; +import org.spongepowered.common.data.persistence.ConfigurateTranslator; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.serialize.SerializationException; +import org.spongepowered.configurate.serialize.TypeSerializer; + +import java.lang.reflect.Type; + +import javax.inject.Inject; + +public final class DataTranslatableTypeSerializer implements TypeSerializer { + + private final DataManager dataManager; + + @Inject + DataTranslatableTypeSerializer(final DataManager dataManager) { + this.dataManager = dataManager; + } + + public boolean accepts(final Type x) { + return this.dataManager.translator(GenericTypeReflector.erase(x)).isPresent(); + } + + @SuppressWarnings("unchecked") + @Override + public Object deserialize(final Type type, final ConfigurationNode value) throws SerializationException { + final Class clazz = (Class) GenericTypeReflector.erase(type); + final DataTranslator translator = this.dataManager.translator(clazz) + .orElseThrow(() -> new SerializationException("Could not translate of DataTranslatable type: " + clazz.getName())); + return translator.translate(ConfigurateTranslator.instance().translate(value)); + } + + @SuppressWarnings("unchecked") + @Override + public void serialize(final Type type, final Object obj, final ConfigurationNode value) throws SerializationException { + if (obj == null) { + value.raw(null); + } else { + final Class clazz = (Class) GenericTypeReflector.erase(type); + final DataTranslator translator = this.dataManager.translator(clazz) + .orElseThrow(() -> new SerializationException("Could not translate of DataTranslatable type: " + clazz.getName())); + ConfigurateTranslator.instance().translateDataToNode(value, translator.translate(obj)); + } + } +} diff --git a/src/main/java/org/spongepowered/common/config/PluginConfigManager.java b/src/main/java/org/spongepowered/common/config/PluginConfigManager.java index 60389388981..0d5514a3150 100644 --- a/src/main/java/org/spongepowered/common/config/PluginConfigManager.java +++ b/src/main/java/org/spongepowered/common/config/PluginConfigManager.java @@ -30,6 +30,7 @@ import org.spongepowered.api.config.ConfigRoot; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.adventure.SpongeAdventure; +import org.spongepowered.common.applaunch.AppLaunch; import org.spongepowered.configurate.ConfigurationOptions; import org.spongepowered.configurate.reference.WatchServiceListener; import org.spongepowered.configurate.serialize.TypeSerializerCollection; @@ -49,30 +50,35 @@ public final class PluginConfigManager implements ConfigManager { private final WatchServiceListener listener; @Inject - PluginConfigManager(final DataSerializableTypeSerializer dataSerializableSerializer) throws IOException { + PluginConfigManager(final DataSerializableTypeSerializer dataSerializableSerializer, final DataTranslatableTypeSerializer dataTranslatableSerializer) throws IOException { // TODO: Move this onto the async scheduler, rather than shared FJ pool? this.listener = WatchServiceListener.builder() .threadFactory(new ThreadFactoryBuilder().setDaemon(true).setNameFormat("Sponge-WatchService-%d").build()) .build(); + AppLaunch.pluginPlatform().addLoaderCloseCallback(this.listener); + this.serializers = TypeSerializerCollection.defaults().childBuilder() // We have a separate type serializer for CatalogTypes, so we explicitly discount them here. // See https://github.com/SpongePowered/SpongeCommon/issues/1348 .register(DataSerializableTypeSerializer::accepts, dataSerializableSerializer) .registerAll(SpongeAdventure.CONFIGURATE.serializers()) + .register(dataTranslatableSerializer::accepts, dataTranslatableSerializer) .build(); } + @SuppressWarnings("deprecation") @Override public ConfigRoot sharedConfig(final PluginContainer container) { - return new PluginConfigRoot(this.serializers, container.metadata().id().toLowerCase(), + return new PluginConfigRoot(this.serializers, container.metadata().originalId().toLowerCase(), SpongeCommon.pluginConfigDirectory()); } + @SuppressWarnings("deprecation") @Override public ConfigRoot pluginConfig(final PluginContainer container) { - return new PluginConfigRoot(this.serializers, container.metadata().id().toLowerCase(), - SpongeCommon.pluginConfigDirectory().resolve(container.metadata().id().toLowerCase())); + return new PluginConfigRoot(this.serializers, container.metadata().originalId().toLowerCase(), + SpongeCommon.pluginConfigDirectory().resolve(container.metadata().originalId().toLowerCase())); } @Override @@ -89,8 +95,4 @@ public static ConfigurationOptions getOptions(final TypeSerializerCollection ser return ConfigurationOptions.defaults() .serializers(serializers); } - - public void close() throws IOException { - this.listener.close(); - } } diff --git a/src/main/java/org/spongepowered/common/config/SpongeGameConfigs.java b/src/main/java/org/spongepowered/common/config/SpongeGameConfigs.java index 246bdef120a..daaff9e848c 100644 --- a/src/main/java/org/spongepowered/common/config/SpongeGameConfigs.java +++ b/src/main/java/org/spongepowered/common/config/SpongeGameConfigs.java @@ -24,15 +24,15 @@ */ package org.spongepowered.common.config; -import net.minecraft.core.registries.Registries; import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.DimensionType; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.util.file.DeleteFileVisitor; import org.spongepowered.api.world.server.ServerWorld; -import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.world.level.storage.ServerLevelDataBridge; import org.spongepowered.common.config.inheritable.BaseConfig; import org.spongepowered.common.config.inheritable.GlobalConfig; @@ -42,9 +42,9 @@ import org.spongepowered.common.launch.config.core.ConfigHandle; import org.spongepowered.common.launch.config.core.SpongeConfigs; import org.spongepowered.common.world.server.SpongeServerLevelData; +import org.spongepowered.configurate.ConfigurationOptions; import java.io.IOException; -import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -62,6 +62,8 @@ public final class SpongeGameConfigs { private static ConfigHandle trackerConfigAdapter; private static volatile InheritableConfigHandle global; + private static final ConfigurationOptions OPTIONS = SpongeConfigs.OPTIONS.serializers(c -> c.registerAll(SpongeAdventure.CONFIGURATE.serializers())); + private SpongeGameConfigs() { } @@ -80,7 +82,7 @@ public static InheritableConfigHandle getForWorld(final net.minecra final SpongeServerLevelData spongeData = ((ServerLevelDataBridge) level.getLevelData()).bridge$spongeData(); if (spongeData.configAdapter() == null) { final ResourceKey worldKey = ((ServerWorld) level).key(); - SpongeCommon.logger().warn("Level data ({}) has no Sponge config but is used by world {} ({}). World config will be reloaded.", level.getLevelData().getClass().getSimpleName(), worldKey, level.getClass().getSimpleName()); + SpongeGameConfigs.LOGGER.warn("Level data ({}) has no Sponge config but is used by world {} ({}). World config will be reloaded.", level.getLevelData().getClass().getSimpleName(), worldKey, level.getClass().getSimpleName()); // A custom level data has been set by a mod, so the key is likely missing too. if (spongeData.key() == null) { @@ -99,34 +101,25 @@ public static boolean doesWorldConfigExist(final ResourceKey world) { public static InheritableConfigHandle load(final DimensionType dimensionType, final ResourceKey world) { // Path format: config/sponge/worlds//.conf - final Path configPath = SpongeConfigs.getDirectory().resolve(Paths.get("worlds", world.namespace(), world.value() + ".conf")); + final Path configFile = SpongeConfigs.getDirectory().resolve(Paths.get("worlds", world.namespace(), world.value() + ".conf")); // Legacy config path: config/sponge/worlds////world.conf - final String legacyName = SpongeGameConfigs.getLegacyWorldName(world); - if (legacyName != null) { - final ResourceKey dimensionTypeKey = (ResourceKey) (Object) SpongeCommon.vanillaRegistry(Registries.DIMENSION_TYPE).getKey(dimensionType); - if (dimensionTypeKey != null) { - final Path legacyPath = SpongeConfigs.getDirectory().resolve(Paths.get("worlds", dimensionTypeKey.namespace(), - SpongeGameConfigs.getLegacyValue(dimensionTypeKey), legacyName, "world.conf")); - if (legacyPath.toFile().isFile() && !configPath.toFile().isFile()) { - try { - Files.createDirectories(configPath.getParent()); - Files.move(legacyPath, configPath); - final Path legacyParent = legacyPath.getParent(); - try (final DirectoryStream str = Files.newDirectoryStream(legacyParent)) { - if (!str.iterator().hasNext()) { - Files.delete(legacyParent); - } - } - } catch (final IOException ex) { - SpongeGameConfigs.LOGGER.error("Unable to migrate config for world {} from legacy location {}", world, legacyPath, ex); - } + final String legacyDimAndName = SpongeGameConfigs.getLegacyDimensionAndName(world); + if (legacyDimAndName != null) { + final Path legacyFile = SpongeConfigs.getDirectory().resolve("worlds/minecraft/" + legacyDimAndName + "/world.conf"); + if (Files.isRegularFile(legacyFile) && !Files.exists(configFile)) { + try { + Files.createDirectories(configFile.getParent()); + Files.move(legacyFile, configFile); + Files.walkFileTree(legacyFile.getParent(), DeleteFileVisitor.INSTANCE); + } catch (final IOException ex) { + SpongeGameConfigs.LOGGER.error("Unable to migrate config for world {} from legacy location {}", world, legacyFile, ex); } } } try { - final InheritableConfigHandle config = new InheritableConfigHandle<>(WorldConfig.class, BaseConfig::transformation, SpongeConfigs.createLoader(configPath), + final InheritableConfigHandle config = new InheritableConfigHandle<>(WorldConfig.class, BaseConfig::transformation, SpongeConfigs.createLoader(configFile, SpongeGameConfigs.OPTIONS), SpongeGameConfigs.getGlobalInheritable()); config.load(); return config; @@ -137,23 +130,14 @@ public static InheritableConfigHandle load(final DimensionType dime } } - @SuppressWarnings("EqualsBetweenInconvertibleTypes") // ResourceKey is ResourceLocation - private static String getLegacyValue(final ResourceKey dimensionType) { - if (dimensionType.equals(Level.NETHER.location())) { - return "nether"; - } else { - return dimensionType.value(); - } - } - - @SuppressWarnings("EqualsBetweenInconvertibleTypes") // ResourceKey is ResourceLocation - private static @Nullable String getLegacyWorldName(final ResourceKey world) { - if (world.equals(Level.OVERWORLD.location())) { - return "world"; - } else if (world.equals(Level.END.location())) { - return "DIM1"; - } else if (world.equals(Level.NETHER.location())) { - return "DIM-1"; + @SuppressWarnings("EqualsBetweenInconvertibleTypes") // ResourceKey is Identifier + private static @Nullable String getLegacyDimensionAndName(final ResourceKey world) { + if (world.equals(Level.OVERWORLD.identifier())) { + return "overworld/world"; + } else if (world.equals(Level.END.identifier())) { + return "the_end/DIM1"; + } else if (world.equals(Level.NETHER.identifier())) { + return "nether/DIM-1"; } return null; } @@ -166,7 +150,7 @@ public static InheritableConfigHandle getGlobalInheritable() { try { SpongeGameConfigs.global = new InheritableConfigHandle<>(GlobalConfig.class, BaseConfig::transformation, - SpongeConfigs.createLoader(SpongeConfigs.getDirectory().resolve(GlobalConfig.FILE_NAME)), null); + SpongeConfigs.createLoader(SpongeConfigs.getDirectory().resolve(GlobalConfig.FILE_NAME), SpongeGameConfigs.OPTIONS), null); SpongeGameConfigs.global.load(); } catch (final IOException e) { SpongeGameConfigs.LOGGER.error("Unable to load global world configuration in {}. Sponge will run with default settings", diff --git a/src/main/java/org/spongepowered/common/config/inheritable/WorldCategory.java b/src/main/java/org/spongepowered/common/config/inheritable/WorldCategory.java index 1d34ce694c7..0bdd833dd02 100644 --- a/src/main/java/org/spongepowered/common/config/inheritable/WorldCategory.java +++ b/src/main/java/org/spongepowered/common/config/inheritable/WorldCategory.java @@ -24,6 +24,8 @@ */ package org.spongepowered.common.config.inheritable; +import net.kyori.adventure.text.Component; +import org.spongepowered.api.world.SerializationBehavior; import org.spongepowered.common.launch.config.common.AutoSaveOptions; import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Comment; @@ -49,4 +51,25 @@ public final class WorldCategory { @Setting("config-auto-save") @Comment("The auto-save options used to save the world configuration files.") public AutoSaveOptions configAutoSave = new AutoSaveOptions(); + + @Setting(value = "load-on-startup") + @Comment("If 'true', this world will load on startup.") + public boolean loadOnStartup = true; + + @Setting(value = "keep-spawn-loaded") + @Comment("If 'true', this worlds spawn will remain loaded with no players.") + public boolean keepSpawnLoaded = true; + + @Setting(value = "pvp-enabled") + @Comment("If 'true', this world will allow PVP combat.") + public Boolean pvpEnabled; + + @Setting(value = "view-distance") + public Integer viewDistance; + + @Setting(value = "serialization-behavior") + public SerializationBehavior serializationBehavior; + + @Setting("display-name") + public Component displayName; } diff --git a/src/main/java/org/spongepowered/common/data/CompoundTagDataView.java b/src/main/java/org/spongepowered/common/data/CompoundTagDataView.java index 8a2a327ccfc..55fe6da6060 100644 --- a/src/main/java/org/spongepowered/common/data/CompoundTagDataView.java +++ b/src/main/java/org/spongepowered/common/data/CompoundTagDataView.java @@ -30,7 +30,6 @@ import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.data.persistence.DataQuery; import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.common.accessor.nbt.CompoundTagAccessor; import org.spongepowered.common.data.persistence.NBTTranslator; import org.spongepowered.common.util.Preconditions; @@ -60,12 +59,12 @@ protected CompoundTagDataView(final CompoundTag compound, final DataView parent, @Override public Stream streamRootKeys() { - return this.compound.getAllKeys().stream().map(k -> k.replace(NBTTranslator.BOOLEAN_IDENTIFIER, "")); + return this.compound.keySet().stream().map(k -> k.replace(NBTTranslator.BOOLEAN_IDENTIFIER, "")); } @Override public Stream> streamRootValues() { - return ((CompoundTagAccessor) this.compound).invoker$entrySet().stream().map( + return this.compound.entrySet().stream().map( e -> Map.entry(e.getKey().replace(NBTTranslator.BOOLEAN_IDENTIFIER, ""), this.fromTagBase(e.getValue(), e.getKey()))); } diff --git a/src/main/java/org/spongepowered/common/data/DataUtil.java b/src/main/java/org/spongepowered/common/data/DataUtil.java index 17480ee478f..41401cd10fd 100644 --- a/src/main/java/org/spongepowered/common/data/DataUtil.java +++ b/src/main/java/org/spongepowered/common/data/DataUtil.java @@ -34,7 +34,6 @@ import org.spongepowered.api.data.persistence.DataView; import org.spongepowered.api.data.persistence.Queries; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.accessor.nbt.CompoundTagAccessor; import org.spongepowered.common.bridge.data.DataCompoundHolder; import org.spongepowered.common.bridge.data.SpongeDataHolderBridge; import org.spongepowered.common.data.persistence.datastore.SpongeDataStore; @@ -59,15 +58,24 @@ public static boolean syncDataToTag(final Object dataHolder) { return false; } + public static DataContainer convertTagToContainer(final CompoundTag compound) { + final DataContainer allData = new CompoundTagDataContainer(compound); + + DataUtil.upgradeDataVersion(compound, allData); // Upgrade v2->v3 + + return allData; + } + public static void deserializeSpongeData(final T dataHolder) { final CompoundTag compound = dataHolder.data$getCompound(); if (compound == null) { return; } - final DataContainer allData = new CompoundTagDataContainer(compound); - DataUtil.upgradeDataVersion(compound, allData); // Upgrade v2->v3 + DataUtil.deserializeSpongeData(dataHolder, DataUtil.convertTagToContainer(compound)); + } + public static void deserializeSpongeData(final T dataHolder, final DataContainer allData) { // Run content-updaters and collect failed data final Class typeToken = dataHolder.getClass().asSubclass(DataHolder.class); allData.getView(Constants.Sponge.Data.V3.SPONGE_DATA_ROOT).ifPresent(customData -> { @@ -162,11 +170,12 @@ private static void upgradeV2CustomData(final CompoundTag compound, final DataCo // cleanup v2 data on compound if (compound.contains(Constants.Forge.FORGE_DATA)) { - final CompoundTag forgeData = compound.getCompound(Constants.Forge.FORGE_DATA); - forgeData.remove(Constants.Sponge.Data.V2.SPONGE_DATA); - if (forgeData.isEmpty()) { - compound.remove(Constants.Forge.FORGE_DATA); - } + compound.getCompound(Constants.Forge.FORGE_DATA).ifPresent(f -> { + f.remove(Constants.Sponge.Data.V2.SPONGE_DATA); + if (f.isEmpty()) { + compound.remove(Constants.Forge.FORGE_DATA); + } + }); } } @@ -177,13 +186,13 @@ public static boolean se } else { // do not modify the original as it might be shared // Copy everything but the sponge root data, we are overwriting. - compound = ((CompoundTagAccessor) compound).invoker$entrySet() + compound = compound.entrySet() .stream() .filter(e -> !e.getKey().equals(Constants.Sponge.Data.V3.SPONGE_DATA_ROOT.asString("."))) .collect( CompoundTag::new, (t, e) -> t.put(e.getKey(), e.getValue().copy()), - (t, o) -> ((CompoundTagAccessor) o).invoker$entrySet().forEach(e -> t.put(e.getKey(), e.getValue().copy())) + (t, o) -> o.entrySet().forEach(e -> t.put(e.getKey(), e.getValue().copy())) ); } diff --git a/src/main/java/org/spongepowered/common/util/AbstractDataPackEntryBuilder.java b/src/main/java/org/spongepowered/common/data/datasync/NoOpSynchronizer.java similarity index 62% rename from src/main/java/org/spongepowered/common/util/AbstractDataPackEntryBuilder.java rename to src/main/java/org/spongepowered/common/data/datasync/NoOpSynchronizer.java index 747d4610344..a859256152a 100644 --- a/src/main/java/org/spongepowered/common/util/AbstractDataPackEntryBuilder.java +++ b/src/main/java/org/spongepowered/common/data/datasync/NoOpSynchronizer.java @@ -22,32 +22,31 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.util; +package org.spongepowered.common.data.datasync; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPackEntry; -import org.spongepowered.api.util.DataPackEntryBuilder; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.server.level.ServerEntity; +import net.minecraft.server.level.ServerPlayer; -import java.util.function.Function; +import java.util.function.Predicate; -public abstract class AbstractDataPackEntryBuilder, B extends DataPackEntryBuilder> - extends AbstractResourceKeyedBuilder implements DataPackEntryBuilder { +public record NoOpSynchronizer() implements ServerEntity.Synchronizer { - protected DataPack pack; + public static final NoOpSynchronizer INSTANCE = new NoOpSynchronizer(); @Override - public B pack(final DataPack pack) { - this.pack = pack; - return (B) this; - } + public void sendToTrackingPlayers(Packet var1) { - public abstract Function valueExtractor(); + } @Override - public B from(final TT value) { - this.fromValue(valueExtractor().apply(value)).pack(value.pack()).key(value.key()); - return (B) this; + public void sendToTrackingPlayersAndSelf(Packet var1) { + } + @Override + public void sendToTrackingPlayersFiltered(Packet var1, Predicate var2) { + } } diff --git a/src/main/java/org/spongepowered/common/data/datasync/VanishedFilteringSynchronizer.java b/src/main/java/org/spongepowered/common/data/datasync/VanishedFilteringSynchronizer.java new file mode 100644 index 00000000000..0219107c052 --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/datasync/VanishedFilteringSynchronizer.java @@ -0,0 +1,75 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.datasync; + +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.server.level.ServerEntity; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import org.spongepowered.common.bridge.data.VanishableBridge; + +import java.lang.ref.WeakReference; +import java.util.function.Predicate; + +public record VanishedFilteringSynchronizer( + ServerEntity.Synchronizer proxy, + WeakReference ref +) implements ServerEntity.Synchronizer { + @Override + public void sendToTrackingPlayers(Packet var1) { + if (this.ref.get() instanceof VanishableBridge vb) { + if (!vb.bridge$vanishState().invisible()) { + this.proxy.sendToTrackingPlayers(var1); + } + return; + } + this.proxy.sendToTrackingPlayers(var1); + } + + @Override + public void sendToTrackingPlayersAndSelf(Packet var1) { + if (this.ref.get() instanceof VanishableBridge vb) { + if (!vb.bridge$vanishState().invisible()) { + this.proxy.sendToTrackingPlayersAndSelf(var1); + } else if (this.ref.get() instanceof final ServerPlayer player) { + player.connection.send(var1); + } + return; + } + this.proxy.sendToTrackingPlayersAndSelf(var1); + } + + @Override + public void sendToTrackingPlayersFiltered(Packet var1, Predicate var2) { + if (this.ref.get() instanceof VanishableBridge vb) { + if (!vb.bridge$vanishState().invisible()) { + this.proxy.sendToTrackingPlayersFiltered(var1, var2); + } + return; + } + this.proxy.sendToTrackingPlayersFiltered(var1, var2); + } +} diff --git a/src/main/java/org/spongepowered/common/data/fixer/CombineMapsCodec.java b/src/main/java/org/spongepowered/common/data/fixer/CombineMapsCodec.java index 95f56113c5c..e6e13cdc5de 100644 --- a/src/main/java/org/spongepowered/common/data/fixer/CombineMapsCodec.java +++ b/src/main/java/org/spongepowered/common/data/fixer/CombineMapsCodec.java @@ -78,10 +78,13 @@ public DataResult decode(final DynamicOps ops, final MapLike input) public RecordBuilder encode(final A input, final DynamicOps ops, final RecordBuilder prefix) { final RecordBuilder firstResult = this.first.encode(input, ops, prefix); - final DataResult secondResult = this.second.encode(this.encodeAction.apply(input), ops, ops.createMap(Collections.emptyMap())); + final B rawSecondData = this.encodeAction.apply(input); + if (rawSecondData != null) { + final DataResult secondResult = this.second.encode(rawSecondData, ops, ops.createMap(Collections.emptyMap())); - // Need an actual result and not a builder: - firstResult.add(this.mergeKey(), secondResult); + // Need an actual result and not a builder: + firstResult.add(this.mergeKey(), secondResult); + } return firstResult; } diff --git a/src/main/java/org/spongepowered/common/data/fixer/entity/EntityTrackedUser.java b/src/main/java/org/spongepowered/common/data/fixer/entity/EntityTrackedUser.java index 7dc5bc68a28..e42ae6c73b8 100644 --- a/src/main/java/org/spongepowered/common/data/fixer/entity/EntityTrackedUser.java +++ b/src/main/java/org/spongepowered/common/data/fixer/entity/EntityTrackedUser.java @@ -41,6 +41,7 @@ public EntityTrackedUser(Schema outputSchema, boolean changesType) { super(outputSchema, changesType); } + @SuppressWarnings("deprecation") @Override protected TypeRewriteRule makeRule() { final Type entityType = this.getInputSchema().getType(References.ENTITY); diff --git a/src/main/java/org/spongepowered/common/data/fixer/world/SpongeLevelFixer.java b/src/main/java/org/spongepowered/common/data/fixer/world/SpongeLevelFixer.java index 08aa997b8c3..e6ffbcbae8a 100644 --- a/src/main/java/org/spongepowered/common/data/fixer/world/SpongeLevelFixer.java +++ b/src/main/java/org/spongepowered/common/data/fixer/world/SpongeLevelFixer.java @@ -49,6 +49,7 @@ public TypeRewriteRule makeRule() { ); } + @SuppressWarnings("deprecation") private Typed fixPlayerIdTable(Typed typed, Type levelType) { Type fieldType = levelType.findFieldType(Constants.Sponge.LEGACY_SPONGE_PLAYER_UUID_TABLE); OpticFinder> listFinder = DSL.fieldFinder(Constants.Sponge.LEGACY_SPONGE_PLAYER_UUID_TABLE, (List.ListType)fieldType); diff --git a/src/main/java/org/spongepowered/common/data/persistence/NBTTranslator.java b/src/main/java/org/spongepowered/common/data/persistence/NBTTranslator.java index cdbcea9e182..f80bf3eadcb 100644 --- a/src/main/java/org/spongepowered/common/data/persistence/NBTTranslator.java +++ b/src/main/java/org/spongepowered/common/data/persistence/NBTTranslator.java @@ -46,7 +46,6 @@ import org.spongepowered.api.data.persistence.DataTranslator; import org.spongepowered.api.data.persistence.DataView; import org.spongepowered.api.data.persistence.InvalidDataException; -import org.spongepowered.common.util.Constants; import java.util.List; import java.util.Map; @@ -107,33 +106,42 @@ public static Tag getBaseFromObject(final Object value) { } else if (value instanceof String) { return StringTag.valueOf((String) value); } else if (value.getClass().isArray()) { - if (value instanceof byte[]) { - return new ByteArrayTag((byte[]) value); - } else if (value instanceof Byte[]) { - byte[] array = new byte[((Byte[]) value).length]; - int counter = 0; - for (Byte data : (Byte[]) value) { - array[counter++] = data; + switch (value) { + case byte[] bytes -> { + return new ByteArrayTag(bytes); } - return new ByteArrayTag(array); - } else if (value instanceof int[]) { - return new IntArrayTag((int[]) value); - } else if (value instanceof Integer[]) { - int[] array = new int[((Integer[]) value).length]; - int counter = 0; - for (Integer data : (Integer[]) value) { - array[counter++] = data; + case Byte[] bytes -> { + byte[] array = new byte[bytes.length]; + int counter = 0; + for (Byte data : bytes) { + array[counter++] = data; + } + return new ByteArrayTag(array); + } + case int[] ints -> { + return new IntArrayTag(ints); + } + case Integer[] integers -> { + int[] array = new int[integers.length]; + int counter = 0; + for (Integer data : integers) { + array[counter++] = data; + } + return new IntArrayTag(array); } - return new IntArrayTag(array); - } else if (value instanceof long[]) { - return new LongArrayTag((long[]) value); - } else if (value instanceof Long[]) { - long[] array = new long[((Long[]) value).length]; - int counter = 0; - for (Long data : (Long[]) value) { - array[counter++] = data; + case long[] longs -> { + return new LongArrayTag(longs); + } + case Long[] longs -> { + long[] array = new long[longs.length]; + int counter = 0; + for (Long data : longs) { + array[counter++] = data; + } + return new LongArrayTag(array); + } + default -> { } - return new LongArrayTag(array); } } else if (value instanceof List) { ListTag list = new ListTag(); @@ -175,118 +183,109 @@ private static DataContainer getViewFromCompound(CompoundTag compound) { } @SuppressWarnings({"unchecked", "rawtypes"}) - private static void setInternal(Tag base, byte type, DataView view, String key) { + private static void setInternal(Tag base, DataView view, String key) { Objects.requireNonNull(base, "base"); Objects.requireNonNull(view, "view"); Objects.requireNonNull(key, "key"); - switch (type) { - case Constants.NBT.TAG_BYTE: + switch (base) { + case ByteTag bt: if (key.contains(NBTTranslator.BOOLEAN_IDENTIFIER)) { - view.set(key.replace(NBTTranslator.BOOLEAN_IDENTIFIER, ""), (((ByteTag) base).getAsByte() != 0)); + view.set(key.replace(NBTTranslator.BOOLEAN_IDENTIFIER, ""), ((bt.byteValue() != 0))); } else { - view.set(key, ((ByteTag) base).getAsByte()); + view.set(key, bt.byteValue()); } break; - case Constants.NBT.TAG_SHORT: - view.set(key, ((ShortTag) base).getAsShort()); + case ShortTag st: + view.set(key, st.shortValue()); break; - case Constants.NBT.TAG_INT: - view.set(key, ((IntTag) base).getAsInt()); + case IntTag it: + view.set(key, it.intValue()); break; - case Constants.NBT.TAG_LONG: - view.set(key, ((LongTag) base).getAsLong()); + case LongTag lt: + view.set(key, lt.longValue()); break; - case Constants.NBT.TAG_FLOAT: - view.set(key, ((FloatTag) base).getAsFloat()); + case FloatTag ft: + view.set(key, ft.floatValue()); break; - case Constants.NBT.TAG_DOUBLE: - view.set(key, ((DoubleTag) base).getAsDouble()); + case DoubleTag dt: + view.set(key, dt.doubleValue()); break; - case Constants.NBT.TAG_BYTE_ARRAY: - view.set(key, ((ByteArrayTag) base).getAsByteArray()); + case ByteArrayTag bat: + view.set(key, bat.getAsByteArray()); break; - case Constants.NBT.TAG_STRING: - view.set(key, base.getAsString()); + case StringTag st: + view.set(key, st.value()); break; - case Constants.NBT.TAG_LIST: + case ListTag lt: ListTag list = (ListTag) base; - byte listType = list.getElementType(); int count = list.size(); List objectList = Lists.newArrayListWithCapacity(count); for (final Tag inbt : list) { - objectList.add(NBTTranslator.fromTagBase(inbt, listType)); + objectList.add(NBTTranslator.fromTagBase(inbt)); } view.set(key, objectList); break; - case Constants.NBT.TAG_COMPOUND: + case CompoundTag ct: DataView internalView = view.createView(key); - CompoundTag compound = (CompoundTag) base; - for (String internalKey : compound.getAllKeys()) { - Tag internalBase = compound.get(internalKey); - byte internalType = internalBase.getId(); + for (String internalKey : ct.keySet()) { + @Nullable Tag internalBase = ct.get(internalKey); + if (internalBase == null) { + continue; + } // Basically.... more recursion. // Reasoning: This avoids creating a new DataContainer which would // then be copied in to the owning DataView anyways. We can internally // set the actual data directly to the child view instead. - NBTTranslator.setInternal(internalBase, internalType, internalView, internalKey); + NBTTranslator.setInternal(internalBase, internalView, internalKey); } break; - case Constants.NBT.TAG_INT_ARRAY: - view.set(key, ((IntArrayTag) base).getAsIntArray()); + case IntArrayTag iat: + view.set(key, iat.getAsIntArray()); break; - case Constants.NBT.TAG_LONG_ARRAY: - view.set(key, ((LongArrayTag) base).getAsLongArray()); + case LongArrayTag lat: + view.set(key, lat.getAsLongArray()); break; default: - throw new IllegalArgumentException("Unknown NBT type " + type); + throw new IllegalArgumentException("Unknown NBT type " + base.getClass().getName()); } } - public static Object fromTagBase(final Tag base, final @Nullable String key, final BiFunction<@Nullable String, CompoundTag, Object> compoundFunction) { - return NBTTranslator.fromTagBase(base, base.getId(), key, compoundFunction); - } - - private static Object fromTagBase(final Tag base, final byte type) { - return NBTTranslator.fromTagBase(base, type, null, (k, c) -> NBTTranslator.getViewFromCompound(c)); + private static Object fromTagBase(final Tag base) { + return NBTTranslator.fromTagBase(base, null, (k, c) -> NBTTranslator.getViewFromCompound(c)); } @SuppressWarnings({"unchecked", "rawtypes"}) - private static Object fromTagBase(final Tag base, final byte type, final @Nullable String key, final BiFunction<@Nullable String, CompoundTag, Object> compoundFunction) { - switch (type) { - case Constants.NBT.TAG_BYTE: - if (key != null && key.contains(NBTTranslator.BOOLEAN_IDENTIFIER)) { - return ((ByteTag) base).getAsByte() != 0; - } - return ((ByteTag) base).getAsByte(); - case Constants.NBT.TAG_SHORT: - return (((ShortTag) base)).getAsShort(); - case Constants.NBT.TAG_INT: - return ((IntTag) base).getAsInt(); - case Constants.NBT.TAG_LONG: - return ((LongTag) base).getAsLong(); - case Constants.NBT.TAG_FLOAT: - return ((FloatTag) base).getAsFloat(); - case Constants.NBT.TAG_DOUBLE: - return ((DoubleTag) base).getAsDouble(); - case Constants.NBT.TAG_BYTE_ARRAY: - return ((ByteArrayTag) base).getAsByteArray(); - case Constants.NBT.TAG_STRING: - return base.getAsString(); - case Constants.NBT.TAG_LIST: - ListTag list = (ListTag) base; - byte listType = list.getElementType(); - int count = list.size(); + public static Object fromTagBase(final Tag base, final @Nullable String key, final BiFunction<@Nullable String, CompoundTag, Object> compoundFunction) { + switch (base) { + case ByteTag bt: + return bt.byteValue(); + case ShortTag st: + return st.shortValue(); + case IntTag it: + return it.intValue(); + case LongTag lt: + return lt.longValue(); + case FloatTag ft: + return ft.floatValue(); + case DoubleTag dt: + return dt.doubleValue(); + case ByteArrayTag bat: + return bat.getAsByteArray(); + case StringTag st: + return st.value(); + case ListTag lt: + int count = lt.size(); List objectList = Lists.newArrayListWithCapacity(count); - for (Tag inbt : list) { - objectList.add(NBTTranslator.fromTagBase(inbt, listType, null, compoundFunction)); + for (Tag inbt : lt) { + objectList.add(NBTTranslator.fromTagBase(inbt, null, compoundFunction)); } return objectList; - case Constants.NBT.TAG_COMPOUND: - return compoundFunction.apply(key, (CompoundTag) base); - case Constants.NBT.TAG_INT_ARRAY: - return ((IntArrayTag) base).getAsIntArray(); - case Constants.NBT.TAG_LONG_ARRAY: - return ((LongArrayTag) base).getAsLongArray(); + case CompoundTag ct: + return compoundFunction.apply(key, ct); + case IntArrayTag iat: + return iat.getAsIntArray(); + case LongArrayTag lat: + return lat.getAsLongArray(); default: return null; } @@ -317,10 +316,10 @@ public DataContainer translate(CompoundTag obj) throws InvalidDataException { @Override public DataView addTo(CompoundTag compound, DataView container) { - for (String key : compound.getAllKeys()) { + for (String key : compound.keySet()) { Tag base = compound.get(key); byte type = base.getId(); - NBTTranslator.setInternal(base, type, container, key); // gotta love recursion + NBTTranslator.setInternal(base, container, key); // gotta love recursion } return container; } diff --git a/src/main/java/org/spongepowered/common/data/persistence/SNBTDataFormat.java b/src/main/java/org/spongepowered/common/data/persistence/SNBTDataFormat.java index 7ebfc654bca..55150e7c6e6 100644 --- a/src/main/java/org/spongepowered/common/data/persistence/SNBTDataFormat.java +++ b/src/main/java/org/spongepowered/common/data/persistence/SNBTDataFormat.java @@ -55,7 +55,7 @@ private static BufferedReader createBufferedReader(final Reader reader) { @Override public DataContainer read(final String input) throws InvalidDataException, IOException { try { - return NBTTranslator.INSTANCE.translate(TagParser.parseTag(input)); + return NBTTranslator.INSTANCE.translate(TagParser.parseCompoundFully(input)); } catch (final CommandSyntaxException e) { throw new InvalidDataException(e); } @@ -65,7 +65,7 @@ public DataContainer read(final String input) throws InvalidDataException, IOExc public DataContainer readFrom(final Reader input) throws InvalidDataException { try { return NBTTranslator.INSTANCE.translate( - TagParser.parseTag(SNBTDataFormat.createBufferedReader(input).lines().collect(Collectors.joining("\n")))); + TagParser.parseCompoundFully(SNBTDataFormat.createBufferedReader(input).lines().collect(Collectors.joining("\n")))); } catch (final CommandSyntaxException e) { throw new InvalidDataException(e); } @@ -74,7 +74,7 @@ public DataContainer readFrom(final Reader input) throws InvalidDataException { @Override public DataContainer readFrom(final InputStream input) throws InvalidDataFormatException { try { - return NBTTranslator.INSTANCE.translate(TagParser.parseTag( + return NBTTranslator.INSTANCE.translate(TagParser.parseCompoundFully( new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n")))); } catch (final CommandSyntaxException e) { throw new InvalidDataException(e); diff --git a/src/main/java/org/spongepowered/common/data/persistence/schematic/SchematicUpdater1_to_2.java b/src/main/java/org/spongepowered/common/data/persistence/schematic/SchematicUpdater1_to_2.java index 83a32b34bb1..ba136624eb0 100644 --- a/src/main/java/org/spongepowered/common/data/persistence/schematic/SchematicUpdater1_to_2.java +++ b/src/main/java/org/spongepowered/common/data/persistence/schematic/SchematicUpdater1_to_2.java @@ -46,7 +46,7 @@ public int outputVersion() { @Override public DataView update(final DataView content) { content.set(Constants.Sponge.Schematic.VERSION, 2); - content.set(Constants.Sponge.Schematic.DATA_VERSION, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + content.set(Constants.Sponge.Schematic.DATA_VERSION, SharedConstants.getCurrentVersion().dataVersion().version()); content.getViewList(Constants.Sponge.Schematic.Versions.V1_TILE_ENTITY_DATA).ifPresent(tiles -> { tiles.forEach(tile -> { // Remove unnecessary version information. diff --git a/src/main/java/org/spongepowered/common/data/provider/GenericMutableDataProviderBase.java b/src/main/java/org/spongepowered/common/data/provider/GenericMutableDataProviderBase.java index e6b66c94410..0056806ef64 100644 --- a/src/main/java/org/spongepowered/common/data/provider/GenericMutableDataProviderBase.java +++ b/src/main/java/org/spongepowered/common/data/provider/GenericMutableDataProviderBase.java @@ -25,6 +25,7 @@ package org.spongepowered.common.data.provider; import io.leangen.geantyref.GenericTypeReflector; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.entity.EntityInLevelCallback; import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.DataTransactionResult; @@ -260,7 +261,7 @@ private Optional> callReplacementEvent( .result(DataTransactionResult.Type.SUCCESS); originalValue.ifPresent(transaction::replace); final ChangeDataHolderEvent.ValueChange valueChange = SpongeEventFactory.createChangeDataHolderEventValueChange( - PhaseTracker.SERVER.currentCause(), + PhaseTracker.getWorldInstance((ServerLevel) entity.world()).currentCause(), transaction.build(), dataHolder); if (SpongeCommon.post(valueChange)) { diff --git a/src/main/java/org/spongepowered/common/data/provider/block/entity/BannerData.java b/src/main/java/org/spongepowered/common/data/provider/block/entity/BannerData.java index 8efdcc317bb..18527783284 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/entity/BannerData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/entity/BannerData.java @@ -30,6 +30,7 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BannerBlockEntity; import net.minecraft.world.level.block.entity.BannerPatternLayers; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.meta.BannerPatternLayer; import org.spongepowered.common.bridge.world.level.block.entity.BannerBlockEntityBridge; @@ -49,29 +50,22 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.BANNER_PATTERN_LAYERS) .get(h -> h.getPatterns().layers().stream().map(BannerPatternLayer.class::cast).toList()) .setAnd((h, v) -> { - final Level world = h.getLevel(); - if (world != null && !world.isClientSide) { // This avoids a client crash because clientside. + final @Nullable Level world = h.getLevel(); + if (world != null && !world.isClientSide()) { // This avoids a client crash because clientside. applyBannerPatternLayers(h, v); return true; } return false; }) + .asImmutable(BannerBlockEntity.class) .create(Keys.DYE_COLOR) .get(h -> ((BannerBlockEntityBridge) h).bridge$getBaseColor()) - .setAnd((h, v) -> { - final Level world = h.getLevel(); - if (world != null && !world.isClientSide) { - ((BannerBlockEntityBridge) h).bridge$setBaseColor(v); - return true; - } - return false; - }); + ; } // @formatter:on private static void applyBannerPatternLayers(final BannerBlockEntity h, final List v) { final DataComponentMap.Builder builder = DataComponentMap.builder(); builder.addAll(h.collectComponents()); - // TODO base color? builder.set(DataComponents.BANNER_PATTERNS, new BannerPatternLayers(v.stream().map(BannerPatternLayers.Layer.class::cast).toList())); final DataComponentMap components = builder.build(); h.applyComponents(components, DataComponentPatch.EMPTY); diff --git a/src/main/java/org/spongepowered/common/data/provider/block/entity/BlockEntityDataProviders.java b/src/main/java/org/spongepowered/common/data/provider/block/entity/BlockEntityDataProviders.java index 72644d0343a..cdb6c976eee 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/entity/BlockEntityDataProviders.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/entity/BlockEntityDataProviders.java @@ -42,6 +42,7 @@ public void registerProviders() { LecternData.register(this.registrator); LockableData.register(this.registrator); MobSpawnerData.register(this.registrator); + PotentSulfurData.register(this.registrator); TrialSpawnerDataProvider.register(this.registrator); SignData.register(this.registrator); SkullData.register(this.registrator); diff --git a/src/main/java/org/spongepowered/common/data/provider/block/entity/CommandBlockData.java b/src/main/java/org/spongepowered/common/data/provider/block/entity/CommandBlockData.java index 0d9edff6663..c5f787d725d 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/entity/CommandBlockData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/entity/CommandBlockData.java @@ -54,7 +54,7 @@ public static void register(final DataProviderRegistrator registrator) { .get(h -> h.getCommandBlock().getSuccessCount()) .set((h, v) -> ((BaseCommandBlockAccessor) h.getCommandBlock()).accessor$successCount(v)) .create(Keys.TRACKS_OUTPUT) - .get(h -> h.getCommandBlock().acceptsFailure()) + .get(h -> h.getCommandBlock().isTrackOutput()) .set((h, v) -> h.getCommandBlock().setTrackOutput(v)); } // @formatter:on diff --git a/src/main/java/org/spongepowered/common/data/provider/block/entity/ConduitData.java b/src/main/java/org/spongepowered/common/data/provider/block/entity/ConduitData.java index f07798b4bc5..e97bccf092a 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/entity/ConduitData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/entity/ConduitData.java @@ -24,7 +24,11 @@ */ package org.spongepowered.common.data.provider.block.entity; +import net.minecraft.world.entity.EntityReference; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.ConduitBlockEntity; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.Entity; import org.spongepowered.common.accessor.world.level.block.entity.ConduitBlockEntityAccessor; @@ -39,7 +43,18 @@ public static void register(final DataProviderRegistrator registrator) { //@formatter:off registrator.asMutable(ConduitBlockEntityAccessor.class) .create(Keys.TARGET_ENTITY) - .get(c -> (Entity) c.accessor$destroyTarget()) + .get(c -> { + final var conduit = (ConduitBlockEntity) c; + final @Nullable EntityReference target = c.accessor$destroyTarget(); + if (target == null) { + return null; + } + final @Nullable Level level = conduit.getLevel(); + if (level == null) { + return null; + } + return (Entity) target.getEntity(level, LivingEntity.class); + }) .deleteAnd(c -> { if (c.accessor$destroyTarget() == null) { return false; @@ -48,10 +63,10 @@ public static void register(final DataProviderRegistrator registrator) { return true; }) .setAnd((c, e) -> { - if (!(e instanceof LivingEntity)) { + if (!(e instanceof LivingEntity le)) { return false; } - c.accessor$setDestroyTarget((LivingEntity) e); + c.accessor$setDestroyTarget(EntityReference.of(le)); return true; }); //@formatter:on diff --git a/src/main/java/org/spongepowered/common/data/provider/block/entity/DecoratedPotData.java b/src/main/java/org/spongepowered/common/data/provider/block/entity/DecoratedPotData.java index 9a91cf6e1fe..0dec24a2483 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/entity/DecoratedPotData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/entity/DecoratedPotData.java @@ -24,6 +24,8 @@ */ package org.spongepowered.common.data.provider.block.entity; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.entity.DecoratedPotBlockEntity; @@ -36,10 +38,19 @@ import org.spongepowered.common.accessor.world.level.block.entity.DecoratedPotBlockEntityAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; public final class DecoratedPotData { + private static final Set> POTTERY_SHERD_ITEMS; + static { + final Set> set = new HashSet<>(); + DecoratedPotPatterns.itemToPatternMappings((itemKey, patternKey) -> set.add(itemKey)); + POTTERY_SHERD_ITEMS = Set.copyOf(set); + } + public static void register(final DataProviderRegistrator registrator) { // @formatter:off registrator.asMutable(DecoratedPotBlockEntity.class) @@ -67,7 +78,8 @@ static boolean setPotDirection(DecoratedPotBlockEntity h, PotDirection direction h.setChanged(); return true; } - if (DecoratedPotPatterns.getPatternFromItem((Item) v) == null) { + final var itemKey = BuiltInRegistries.ITEM.getResourceKey((Item) v).orElse(null); + if (itemKey == null || !POTTERY_SHERD_ITEMS.contains(itemKey)) { return false; } var current = h.getDecorations(); diff --git a/src/main/java/org/spongepowered/common/data/provider/block/entity/LockableData.java b/src/main/java/org/spongepowered/common/data/provider/block/entity/LockableData.java index cbd0fae9ef6..cdbbb388550 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/entity/LockableData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/entity/LockableData.java @@ -24,7 +24,6 @@ */ package org.spongepowered.common.data.provider.block.entity; -import net.minecraft.world.LockCode; import org.spongepowered.api.data.Keys; import org.spongepowered.common.accessor.world.level.block.entity.BaseContainerBlockEntityAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -38,9 +37,9 @@ private LockableData() { public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(BaseContainerBlockEntityAccessor.class) - .create(Keys.LOCK_TOKEN) - .get(h -> h.accessor$lockKey().key()) - .set((h, v) -> h.accessor$lockKey(v.isEmpty() ? LockCode.NO_LOCK : new LockCode(v))); + .create(Keys.LOCK_TOKEN);// TODO - Implement Item Predicates +// .get(h -> h.accessor$lockKey().key()) +// .set((h, v) -> h.accessor$lockKey(v.isEmpty() ? LockCode.NO_LOCK : new LockCode(v))); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/block/entity/PotentSulfurData.java b/src/main/java/org/spongepowered/common/data/provider/block/entity/PotentSulfurData.java new file mode 100644 index 00000000000..fbffa6fabff --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/provider/block/entity/PotentSulfurData.java @@ -0,0 +1,57 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.provider.block.entity; + +import net.minecraft.world.level.block.entity.PotentSulfurBlockEntity; +import org.spongepowered.api.data.Keys; +import org.spongepowered.common.data.provider.DataProviderRegistrator; +import org.spongepowered.common.util.SpongeTicks; + +public final class PotentSulfurData { + + private PotentSulfurData() { + } + + // @formatter:off + public static void register(final DataProviderRegistrator registrator) { + registrator + .asMutable(PotentSulfurBlockEntity.class) + .create(Keys.ERUPTION_COUNTDOWN) + .get(h -> h.waitingCountdown < 0 ? null : new SpongeTicks(h.waitingCountdown)) + .setAnd((h, v) -> { + if (v.isInfinite()) { + return false; + } + final int ticks = SpongeTicks.toSaturatedIntOrInfinite(v); + if (ticks < 0) { + return false; + } + h.waitingCountdown = ticks; + return true; + }) + .delete(PotentSulfurBlockEntity::resetCountdown); + } + // @formatter:on +} diff --git a/src/main/java/org/spongepowered/common/data/provider/block/entity/SkullData.java b/src/main/java/org/spongepowered/common/data/provider/block/entity/SkullData.java index a1736b556c5..c7312c6f97a 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/entity/SkullData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/entity/SkullData.java @@ -24,7 +24,9 @@ */ package org.spongepowered.common.data.provider.block.entity; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.core.component.DataComponentPatch; +import net.minecraft.core.component.DataComponents; +import net.minecraft.resources.Identifier; import net.minecraft.world.item.component.ResolvableProfile; import net.minecraft.world.level.block.entity.SkullBlockEntity; import org.spongepowered.api.ResourceKey; @@ -33,6 +35,8 @@ import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.profile.SpongeGameProfile; +import java.util.UUID; + public final class SkullData { private SkullData() { @@ -45,16 +49,22 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.GAME_PROFILE) .get(h -> { if (h.getOwnerProfile() != null) { - return SpongeGameProfile.of(h.getOwnerProfile().gameProfile()); + return SpongeGameProfile.of(h.getOwnerProfile().partialProfile()); } return null; }) - .set((h, v) -> h.setOwner(new ResolvableProfile(SpongeGameProfile.toMcProfile(v)))) - .delete(h -> h.setOwner(null)) + .set((h, v) -> { + h.applyComponents(h.components(), DataComponentPatch.builder() + .set(DataComponents.PROFILE, ResolvableProfile.createResolved(SpongeGameProfile.toMcProfile(v))) + .build()); + }) + .delete(h -> h.applyComponents(h.components(), DataComponentPatch.builder() + .set(DataComponents.PROFILE, ResolvableProfile.createUnresolved(new UUID(0, 0))) + .build())) .asMutable(SkullBlockEntityAccessor.class) .create(Keys.NOTE_BLOCK_SOUND) .get(h -> (ResourceKey) (Object) h.accessor$noteBlockSound()) - .set((h, v) -> h.accessor$noteBlockSound((ResourceLocation) (Object) v)) + .set((h, v) -> h.accessor$noteBlockSound((Identifier) (Object) v)) .delete(h -> h.accessor$noteBlockSound(null)) ; } diff --git a/src/main/java/org/spongepowered/common/data/provider/block/entity/TrialSpawnerDataProvider.java b/src/main/java/org/spongepowered/common/data/provider/block/entity/TrialSpawnerDataProvider.java index 2c8195bd761..b5c03f3d7cf 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/entity/TrialSpawnerDataProvider.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/entity/TrialSpawnerDataProvider.java @@ -24,7 +24,9 @@ */ package org.spongepowered.common.data.provider.block.entity; +import net.minecraft.core.Holder; import net.minecraft.world.level.block.entity.TrialSpawnerBlockEntity; +import net.minecraft.world.level.block.entity.trialspawner.TrialSpawner; import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerConfig; import org.spongepowered.api.data.Keys; import org.spongepowered.common.accessor.world.level.block.entity.trialspawner.TrialSpawnerAccessor; @@ -45,54 +47,56 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(TrialSpawnerBlockEntity.class) .create(Keys.MAX_NEARBY_ENTITIES) - .get(h -> h.getTrialSpawner().getConfig().calculateTargetSimultaneousMobs(h.getTrialSpawner().getData().countAdditionalPlayers(h.getBlockPos()))) + .get(h -> h.getTrialSpawner().activeConfig().calculateTargetSimultaneousMobs(h.getTrialSpawner().getStateData().countAdditionalPlayers(h.getBlockPos()))) .create(Keys.NEXT_ENTITY_TO_SPAWN) - .get(h -> ((TrialSpawnerDataAccessor) h.getTrialSpawner().getData()).accessor$nextSpawnData().map(EntityUtil::toArchetype).orElse(null)) - .set((h, v) -> ((TrialSpawnerDataAccessor) h.getTrialSpawner().getData()).accessor$nextSpawnData(Optional.of(EntityUtil.toSpawnData(v)))) - .delete(h -> ((TrialSpawnerDataAccessor) h.getTrialSpawner().getData()).accessor$nextSpawnData(Optional.empty())) + .get(h -> ((TrialSpawnerDataAccessor) h.getTrialSpawner().getStateData()).accessor$nextSpawnData().map(EntityUtil::toArchetype).orElse(null)) + .set((h, v) -> ((TrialSpawnerDataAccessor) h.getTrialSpawner().getStateData()).accessor$nextSpawnData(Optional.of(EntityUtil.toSpawnData(v)))) + .delete(h -> ((TrialSpawnerDataAccessor) h.getTrialSpawner().getStateData()).accessor$nextSpawnData(Optional.empty())) .create(Keys.REMAINING_SPAWN_DELAY) - .get(h -> new SpongeTicks(((TrialSpawnerDataAccessor) h.getTrialSpawner().getData()).accessor$nextMobSpawnsAt() - h.getLevel().getGameTime())) + .get(h -> new SpongeTicks(((TrialSpawnerDataAccessor) h.getTrialSpawner().getStateData()).accessor$nextMobSpawnsAt() - h.getLevel().getGameTime())) .setAnd((h, v) -> { if (v.isInfinite()) { return false; } - ((TrialSpawnerDataAccessor) h.getTrialSpawner().getData()).accessor$nextMobSpawnsAt(h.getLevel().getGameTime() + v.ticks()); + ((TrialSpawnerDataAccessor) h.getTrialSpawner().getStateData()).accessor$nextMobSpawnsAt(h.getLevel().getGameTime() + v.ticks()); return true; }) .create(Keys.SPAWN_RANGE) - .get(h -> (double) h.getTrialSpawner().getConfig().spawnRange()) + .get(h -> (double) h.getTrialSpawner().activeConfig().spawnRange()) .set((h, v) -> { - final var normalConfig = h.getTrialSpawner().getNormalConfig(); - final var ominousConfig = h.getTrialSpawner().getOminousConfig(); + final var trialSpawner = h.getTrialSpawner(); + final var normalConfig = trialSpawner.normalConfig(); + final var ominousConfig = trialSpawner.ominousConfig(); final var newNormalConfig = new TrialSpawnerConfig(v.intValue(), normalConfig.totalMobs(), normalConfig.simultaneousMobs(), normalConfig.totalMobsAddedPerPlayer(), normalConfig.simultaneousMobsAddedPerPlayer(), normalConfig.ticksBetweenSpawn(), normalConfig.spawnPotentialsDefinition(), normalConfig.lootTablesToEject(), normalConfig.itemsToDropWhenOminous()); final var newOminiousConfig = new TrialSpawnerConfig(v.intValue(), ominousConfig.totalMobs(), ominousConfig.simultaneousMobs(), ominousConfig.totalMobsAddedPerPlayer(), ominousConfig.simultaneousMobsAddedPerPlayer(), ominousConfig.ticksBetweenSpawn(), ominousConfig.spawnPotentialsDefinition(), ominousConfig.lootTablesToEject(), ominousConfig.itemsToDropWhenOminous()); - final var accessor = (TrialSpawnerAccessor) (Object) h.getTrialSpawner(); - accessor.accessor$normalConfig(newNormalConfig); - accessor.accessor$ominousConfig(newOminiousConfig); + final var accessor = (TrialSpawnerAccessor) (Object) trialSpawner; + final var full = new TrialSpawner.FullConfig(Holder.direct(newNormalConfig), Holder.direct(newOminiousConfig), trialSpawner.getTargetCooldownLength(), trialSpawner.getRequiredPlayerRange()); + accessor.accessor$config(full); }) // TODO totalMobs // TODO simultaneousMobs // TODO totalMobsAddedPerPlayer // TODO simultaneousMobsAddedPerPlayer .create(Keys.MAX_SPAWN_DELAY) - .get(h -> new SpongeTicks(h.getTrialSpawner().getConfig().ticksBetweenSpawn())) + .get(h -> new SpongeTicks(h.getTrialSpawner().activeConfig().ticksBetweenSpawn())) // TODO set ticksBetweenSpawn .create(Keys.MIN_SPAWN_DELAY) - .get(h -> new SpongeTicks(h.getTrialSpawner().getConfig().ticksBetweenSpawn())) + .get(h -> new SpongeTicks(h.getTrialSpawner().activeConfig().ticksBetweenSpawn())) .create(Keys.SPAWNABLE_ENTITIES) - .get(h -> EntityUtil.toWeightedArchetypes(h.getTrialSpawner().getConfig().spawnPotentialsDefinition())) + .get(h -> EntityUtil.toWeightedArchetypes(h.getTrialSpawner().activeConfig().spawnPotentialsDefinition())) .set((h, v) -> { final var spawnPotentials = EntityUtil.toSpawnPotentials(v); - final var normalConfig = h.getTrialSpawner().getNormalConfig(); - final var ominousConfig = h.getTrialSpawner().getOminousConfig(); + final var trialSpawner = h.getTrialSpawner(); + final var normalConfig = h.getTrialSpawner().normalConfig(); + final var ominousConfig = h.getTrialSpawner().ominousConfig(); final var newNormalConfig = new TrialSpawnerConfig(normalConfig.spawnRange(), normalConfig.totalMobs(), normalConfig.simultaneousMobs(), normalConfig.totalMobsAddedPerPlayer(), normalConfig.simultaneousMobsAddedPerPlayer(), normalConfig.ticksBetweenSpawn(), spawnPotentials, normalConfig.lootTablesToEject(), normalConfig.itemsToDropWhenOminous()); final var newOminiousConfig = new TrialSpawnerConfig(ominousConfig.spawnRange(), ominousConfig.totalMobs(), ominousConfig.simultaneousMobs(), ominousConfig.totalMobsAddedPerPlayer(), ominousConfig.simultaneousMobsAddedPerPlayer(), ominousConfig.ticksBetweenSpawn(), spawnPotentials, ominousConfig.lootTablesToEject(), ominousConfig.itemsToDropWhenOminous()); final var accessor = (TrialSpawnerAccessor) (Object) h.getTrialSpawner(); - accessor.accessor$normalConfig(newNormalConfig); - accessor.accessor$ominousConfig(newOminiousConfig); + final var full = new TrialSpawner.FullConfig(Holder.direct(newNormalConfig), Holder.direct(newOminiousConfig), trialSpawner.getTargetCooldownLength(), trialSpawner.getRequiredPlayerRange()); + accessor.accessor$config(full); }); // TODO lootTablesToEject // TODO itemsToDropWhenOminous diff --git a/src/main/java/org/spongepowered/common/data/provider/block/location/ServerLocationData.java b/src/main/java/org/spongepowered/common/data/provider/block/location/ServerLocationData.java index ec9e9c2ae55..2f3915a3afd 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/location/ServerLocationData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/location/ServerLocationData.java @@ -34,6 +34,7 @@ import org.spongepowered.api.block.entity.NameableBlockEntity; import org.spongepowered.api.data.Keys; import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.common.accessor.world.level.biome.BiomeAccessor; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.CustomNameableBridge; import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge; @@ -66,7 +67,7 @@ public static void register(final DataProviderRegistrator registrator) { final Level world = (Level) h.world(); final BlockPos pos = VecHelper.toBlockPos(h); final Biome biome = world.getBiome(pos).unwrap().right().get(); - return (double) biome.getTemperature(pos); + return (double) ((BiomeAccessor) (Object) biome).shadow$getTemperature(pos, world.getSeaLevel()); }) .create(Keys.SKY_LIGHT) .get(h -> { @@ -79,7 +80,7 @@ public static void register(final DataProviderRegistrator registrator) { final BlockState block = (BlockState) h.block(); final Level world = (Level) h.world(); final BlockPos pos = VecHelper.toBlockPos(h.position()); - return block.isSolidRender(world, pos); + return block.isSolidRender(); }) .create(Keys.IS_INDIRECTLY_POWERED) .get(h -> { diff --git a/src/main/java/org/spongepowered/common/data/provider/block/state/BlockData.java b/src/main/java/org/spongepowered/common/data/provider/block/state/BlockData.java index 9d7066fdf5a..691deaaecb4 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/state/BlockData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/state/BlockData.java @@ -54,6 +54,7 @@ private BlockData() { } // @formatter:off + @SuppressWarnings("deprecation") public static void register(final DataProviderRegistrator registrator) { registrator .asImmutable(Block.class) diff --git a/src/main/java/org/spongepowered/common/data/provider/block/state/BlockStateDataProvider.java b/src/main/java/org/spongepowered/common/data/provider/block/state/BlockStateDataProvider.java index 75e960d2c90..34ec0ae3da7 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/state/BlockStateDataProvider.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/state/BlockStateDataProvider.java @@ -66,14 +66,11 @@ public static void register(final DataProviderRegistrator registrator) { BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CANDLES, BlockStateProperties.CANDLES); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CAN_SUMMON, BlockStateProperties.CAN_SUMMON); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CHEST_TYPE, BlockStateProperties.CHEST_TYPE); - BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CHISELED_BOOKSHELF_SLOT_0_OCCUPIED, BlockStateProperties.CHISELED_BOOKSHELF_SLOT_0_OCCUPIED); - BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CHISELED_BOOKSHELF_SLOT_1_OCCUPIED, BlockStateProperties.CHISELED_BOOKSHELF_SLOT_1_OCCUPIED); - BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CHISELED_BOOKSHELF_SLOT_2_OCCUPIED, BlockStateProperties.CHISELED_BOOKSHELF_SLOT_2_OCCUPIED); - BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CHISELED_BOOKSHELF_SLOT_3_OCCUPIED, BlockStateProperties.CHISELED_BOOKSHELF_SLOT_3_OCCUPIED); - BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CHISELED_BOOKSHELF_SLOT_4_OCCUPIED, BlockStateProperties.CHISELED_BOOKSHELF_SLOT_4_OCCUPIED); - BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CHISELED_BOOKSHELF_SLOT_5_OCCUPIED, BlockStateProperties.CHISELED_BOOKSHELF_SLOT_5_OCCUPIED); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CONDITIONAL, BlockStateProperties.CONDITIONAL); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.COPPER_GOLEM_POSE, BlockStateProperties.COPPER_GOLEM_POSE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CRACKED, BlockStateProperties.CRACKED); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CRAFTING, BlockStateProperties.CRAFTING); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.CREAKING_HEART_STATE, BlockStateProperties.CREAKING_HEART_STATE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.DELAY, BlockStateProperties.DELAY); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.DISARMED, BlockStateProperties.DISARMED); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.DISTANCE, BlockStateProperties.DISTANCE); @@ -81,7 +78,7 @@ public static void register(final DataProviderRegistrator registrator) { BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.DOUBLE_BLOCK_HALF, BlockStateProperties.DOUBLE_BLOCK_HALF); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.DOWN, BlockStateProperties.DOWN); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.DRAG, BlockStateProperties.DRAG); - BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.DRIPSTONE_THICKNESS, BlockStateProperties.DRIPSTONE_THICKNESS); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.DRIED_GHAST_HYDRATION_LEVELS, BlockStateProperties.DRIED_GHAST_HYDRATION_LEVELS); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.DUSTED, BlockStateProperties.DUSTED); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.EAST, BlockStateProperties.EAST); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.EAST_REDSTONE, BlockStateProperties.EAST_REDSTONE); @@ -114,19 +111,23 @@ public static void register(final DataProviderRegistrator registrator) { BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.LEVEL_HONEY, BlockStateProperties.LEVEL_HONEY); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.LIT, BlockStateProperties.LIT); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.LOCKED, BlockStateProperties.LOCKED); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.MAP, BlockStateProperties.MAP); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.MODE_COMPARATOR, BlockStateProperties.MODE_COMPARATOR); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.MOISTURE, BlockStateProperties.MOISTURE); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.NATURAL, BlockStateProperties.NATURAL); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.NORTH, BlockStateProperties.NORTH); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.NORTH_REDSTONE, BlockStateProperties.NORTH_REDSTONE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.NORTH_WALL, BlockStateProperties.NORTH_WALL); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.NOTE, BlockStateProperties.NOTE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.NOTEBLOCK_INSTRUMENT, BlockStateProperties.NOTEBLOCK_INSTRUMENT); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.OCCUPIED, BlockStateProperties.OCCUPIED); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.OMINOUS, BlockStateProperties.OMINOUS); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.OPEN, BlockStateProperties.OPEN); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.ORIENTATION, BlockStateProperties.ORIENTATION); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.PERSISTENT, BlockStateProperties.PERSISTENT); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.PICKLES, BlockStateProperties.PICKLES); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.PISTON_TYPE, BlockStateProperties.PISTON_TYPE); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.POTENT_SULFUR_STATE, BlockStateProperties.POTENT_SULFUR_STATE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.POWER, BlockStateProperties.POWER); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.POWERED, BlockStateProperties.POWERED); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.RAIL_SHAPE, BlockStateProperties.RAIL_SHAPE); @@ -134,30 +135,41 @@ public static void register(final DataProviderRegistrator registrator) { BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.RESPAWN_ANCHOR_CHARGES, BlockStateProperties.RESPAWN_ANCHOR_CHARGES); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.ROTATION_16, BlockStateProperties.ROTATION_16); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SCULK_SENSOR_PHASE, BlockStateProperties.SCULK_SENSOR_PHASE); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SEGMENT_AMOUNT, BlockStateProperties.SEGMENT_AMOUNT); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SHORT, BlockStateProperties.SHORT); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SHRIEKING, BlockStateProperties.SHRIEKING); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SIDE_CHAIN_PART, BlockStateProperties.SIDE_CHAIN_PART); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SIGNAL_FIRE, BlockStateProperties.SIGNAL_FIRE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SLAB_TYPE, BlockStateProperties.SLAB_TYPE); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SLOT_0_OCCUPIED, BlockStateProperties.SLOT_0_OCCUPIED); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SLOT_1_OCCUPIED, BlockStateProperties.SLOT_1_OCCUPIED); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SLOT_2_OCCUPIED, BlockStateProperties.SLOT_2_OCCUPIED); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SLOT_3_OCCUPIED, BlockStateProperties.SLOT_3_OCCUPIED); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SLOT_4_OCCUPIED, BlockStateProperties.SLOT_4_OCCUPIED); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SLOT_5_OCCUPIED, BlockStateProperties.SLOT_5_OCCUPIED); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SNOWY, BlockStateProperties.SNOWY); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SOUTH, BlockStateProperties.SOUTH); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SOUTH_REDSTONE, BlockStateProperties.SOUTH_REDSTONE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SOUTH_WALL, BlockStateProperties.SOUTH_WALL); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.SPELEOTHEM_THICKNESS, BlockStateProperties.SPELEOTHEM_THICKNESS); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.STABILITY_DISTANCE, BlockStateProperties.STABILITY_DISTANCE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.STAGE, BlockStateProperties.STAGE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.STAIRS_SHAPE, BlockStateProperties.STAIRS_SHAPE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.STRUCTUREBLOCK_MODE, BlockStateProperties.STRUCTUREBLOCK_MODE); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.TEST_BLOCK_MODE, BlockStateProperties.TEST_BLOCK_MODE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.TILT, BlockStateProperties.TILT); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.TIP, BlockStateProperties.TIP); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.TRIAL_SPAWNER_STATE, BlockStateProperties.TRIAL_SPAWNER_STATE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.TRIGGERED, BlockStateProperties.TRIGGERED); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.UNSTABLE, BlockStateProperties.UNSTABLE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.UP, BlockStateProperties.UP); + BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.VAULT_STATE, BlockStateProperties.VAULT_STATE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.VERTICAL_DIRECTION, BlockStateProperties.VERTICAL_DIRECTION); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.WATERLOGGED, BlockStateProperties.WATERLOGGED); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.WEST, BlockStateProperties.WEST); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.WEST_REDSTONE, BlockStateProperties.WEST_REDSTONE); BlockStateDataProvider.registerProperty(registrator, BlockStateKeys.WEST_WALL, BlockStateProperties.WEST_WALL); - registrator.asImmutable(BlockState.class) - .create(Keys.PUSH_REACTION) - .get(h -> (PushReaction) (Object) h.getPistonPushReaction()); + registrator.asImmutable(BlockState.class).create(Keys.PUSH_REACTION).get(h -> (PushReaction) (Object) h.getPistonPushReaction()); } // @formatter:off @@ -176,5 +188,4 @@ private static , V extends Comparable> void registerP .set((is, v) -> is.update(DataComponents.BLOCK_STATE, BlockItemStateProperties.EMPTY, c -> c.with((Property) property, v))); } // @formatter:on - } diff --git a/src/main/java/org/spongepowered/common/data/provider/block/state/FarmlandData.java b/src/main/java/org/spongepowered/common/data/provider/block/state/FarmlandData.java index 24a93f29e8e..b72a93ef5fa 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/state/FarmlandData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/state/FarmlandData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.block.state; -import net.minecraft.world.level.block.FarmBlock; +import net.minecraft.world.level.block.FarmlandBlock; import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -40,10 +40,10 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asImmutable(BlockState.class) .create(Keys.MOISTURE) - .constructValue((h, v) -> BoundedUtil.constructImmutableValueInteger(v, Keys.MOISTURE, FarmBlock.MOISTURE)) - .get(h -> h.getValue(FarmBlock.MOISTURE)) - .set((h, v) -> BoundedUtil.setInteger(h, v, FarmBlock.MOISTURE)) - .supports(h -> h.getBlock() instanceof FarmBlock); + .constructValue((h, v) -> BoundedUtil.constructImmutableValueInteger(v, Keys.MOISTURE, FarmlandBlock.MOISTURE)) + .get(h -> h.getValue(FarmlandBlock.MOISTURE)) + .set((h, v) -> BoundedUtil.setInteger(h, v, FarmlandBlock.MOISTURE)) + .supports(h -> h.getBlock() instanceof FarmlandBlock); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/block/state/MovingPistonData.java b/src/main/java/org/spongepowered/common/data/provider/block/state/MovingPistonData.java index c5e3f41c9dd..c1e81e95837 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/state/MovingPistonData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/state/MovingPistonData.java @@ -25,6 +25,7 @@ package org.spongepowered.common.data.provider.block.state; import net.minecraft.world.level.block.piston.MovingPistonBlock; +import net.minecraft.world.level.block.piston.PistonMovingBlockEntity; import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -42,7 +43,13 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.DIRECTION) .get(h -> DirectionUtil.getFor(h.getValue(MovingPistonBlock.FACING))) .set((h, v) -> DirectionUtil.set(h, v, MovingPistonBlock.FACING)) - .supports(h -> h.getBlock() instanceof MovingPistonBlock); + .supports(h -> h.getBlock() instanceof MovingPistonBlock) + .asImmutable(PistonMovingBlockEntity.class) + .create(Keys.BLOCK_STATE) + .get(h -> (org.spongepowered.api.block.BlockState) h.getMovedState()) + .create(Keys.IS_EXTENDED) + .get(PistonMovingBlockEntity::isExtending) + ; } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/block/state/SnowyDirtData.java b/src/main/java/org/spongepowered/common/data/provider/block/state/SnowyDirtData.java index fbdd221aea0..12886cba8f5 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/state/SnowyDirtData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/state/SnowyDirtData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.block.state; -import net.minecraft.world.level.block.SnowyDirtBlock; +import net.minecraft.world.level.block.SnowyBlock; import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -39,9 +39,9 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asImmutable(BlockState.class) .create(Keys.IS_SNOWY) - .get(h -> h.getValue(SnowyDirtBlock.SNOWY)) - .set((h, v) -> h.setValue(SnowyDirtBlock.SNOWY, v)) - .supports(h -> h.getBlock() instanceof SnowyDirtBlock); + .get(h -> h.getValue(SnowyBlock.SNOWY)) + .set((h, v) -> h.setValue(SnowyBlock.SNOWY, v)) + .supports(h -> h.getBlock() instanceof SnowyBlock); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/block/state/WallData.java b/src/main/java/org/spongepowered/common/data/provider/block/state/WallData.java index c6d8332d720..fee457b8f63 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/state/WallData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/state/WallData.java @@ -42,32 +42,32 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asImmutable(BlockState.class) .create(Keys.CONNECTED_DIRECTIONS) - .get(h -> DirectionalUtil.getHorizontalUpFrom(h, WallBlock.EAST_WALL, WallBlock.WEST_WALL, WallBlock.NORTH_WALL, WallBlock.SOUTH_WALL, WallBlock.UP)) - .set((h, v) -> DirectionalUtil.setHorizontalUpFor(h, v, WallBlock.EAST_WALL, WallBlock.WEST_WALL, WallBlock.NORTH_WALL, WallBlock.SOUTH_WALL, WallBlock.UP)) + .get(h -> DirectionalUtil.getHorizontalUpFrom(h, WallBlock.EAST, WallBlock.WEST, WallBlock.NORTH, WallBlock.SOUTH, WallBlock.UP)) + .set((h, v) -> DirectionalUtil.setHorizontalUpFor(h, v, WallBlock.EAST, WallBlock.WEST, WallBlock.NORTH, WallBlock.SOUTH, WallBlock.UP)) .supports(h -> h.getBlock() instanceof WallBlock) .create(Keys.IS_CONNECTED_EAST) - .get(h -> h.getValue(WallBlock.EAST_WALL) != WallSide.NONE) - .set((h, v) -> WallData.setWallSideProperty(h, WallBlock.EAST_WALL, v)) + .get(h -> h.getValue(WallBlock.EAST) != WallSide.NONE) + .set((h, v) -> WallData.setWallSideProperty(h, WallBlock.EAST, v)) .supports(h -> h.getBlock() instanceof WallBlock) .create(Keys.IS_CONNECTED_NORTH) - .get(h -> h.getValue(WallBlock.NORTH_WALL) != WallSide.NONE) - .set((h, v) -> WallData.setWallSideProperty(h, WallBlock.NORTH_WALL, v)) + .get(h -> h.getValue(WallBlock.NORTH) != WallSide.NONE) + .set((h, v) -> WallData.setWallSideProperty(h, WallBlock.NORTH, v)) .supports(h -> h.getBlock() instanceof WallBlock) .create(Keys.IS_CONNECTED_SOUTH) - .get(h -> h.getValue(WallBlock.SOUTH_WALL) != WallSide.NONE) - .set((h, v) -> WallData.setWallSideProperty(h, WallBlock.SOUTH_WALL, v)) + .get(h -> h.getValue(WallBlock.SOUTH) != WallSide.NONE) + .set((h, v) -> WallData.setWallSideProperty(h, WallBlock.SOUTH, v)) .supports(h -> h.getBlock() instanceof WallBlock) .create(Keys.IS_CONNECTED_UP) .get(h -> h.getValue(WallBlock.UP)) .set((h, v) -> h.setValue(WallBlock.UP, v)) .supports(h -> h.getBlock() instanceof WallBlock) .create(Keys.IS_CONNECTED_WEST) - .get(h -> h.getValue(WallBlock.WEST_WALL) != WallSide.NONE) - .set((h, v) -> WallData.setWallSideProperty(h, WallBlock.WEST_WALL, v)) + .get(h -> h.getValue(WallBlock.WEST) != WallSide.NONE) + .set((h, v) -> WallData.setWallSideProperty(h, WallBlock.WEST, v)) .supports(h -> h.getBlock() instanceof WallBlock) .create(Keys.WALL_CONNECTION_STATES) - .get(h -> DirectionalUtil.getHorizontalFrom(h, WallBlock.EAST_WALL, WallBlock.WEST_WALL, WallBlock.NORTH_WALL, WallBlock.SOUTH_WALL)) - .set((h, v) -> DirectionalUtil.setHorizontalFor(h, v, WallBlock.EAST_WALL, WallBlock.WEST_WALL, WallBlock.NORTH_WALL, WallBlock.SOUTH_WALL)) + .get(h -> DirectionalUtil.getHorizontalFrom(h, WallBlock.EAST, WallBlock.WEST, WallBlock.NORTH, WallBlock.SOUTH)) + .set((h, v) -> DirectionalUtil.setHorizontalFor(h, v, WallBlock.EAST, WallBlock.WEST, WallBlock.NORTH, WallBlock.SOUTH)) .supports(h -> h.getBlock() instanceof WallBlock) ; } diff --git a/src/main/java/org/spongepowered/common/data/provider/block/state/WoolData.java b/src/main/java/org/spongepowered/common/data/provider/block/state/WoolData.java index b0f500b4ea4..d6c218d3338 100644 --- a/src/main/java/org/spongepowered/common/data/provider/block/state/WoolData.java +++ b/src/main/java/org/spongepowered/common/data/provider/block/state/WoolData.java @@ -27,7 +27,6 @@ import net.minecraft.world.level.block.Block; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.DyeColor; -import org.spongepowered.common.accessor.world.entity.animal.SheepAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.util.DyeColorUtil; @@ -42,7 +41,7 @@ public static void register(final DataProviderRegistrator registrator) { .asImmutable(Block.class) .create(Keys.DYE_COLOR) .get(h -> (DyeColor) (Object) DyeColorUtil.COLOR_BY_WOOL.get(h)) - .supports(h -> SheepAccessor.accessor$ITEM_BY_DYE().containsValue(h)); + .supports(DyeColorUtil.COLOR_BY_WOOL::containsKey); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/AbstractArrowData.java b/src/main/java/org/spongepowered/common/data/provider/entity/AbstractArrowData.java index 2ee80e000ee..da8e6203e2b 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/AbstractArrowData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/AbstractArrowData.java @@ -24,9 +24,10 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.entity.projectile.arrow.AbstractArrow; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.PickupRule; +import org.spongepowered.common.accessor.world.entity.projectile.arrow.AbstractArrowAccessor; import org.spongepowered.common.bridge.world.entity.projectile.AbstractArrowBridge; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -40,7 +41,7 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(AbstractArrow.class) .create(Keys.ATTACK_DAMAGE) - .get(AbstractArrow::getBaseDamage) + .get(a -> ((AbstractArrowAccessor) a).accessor$baseDamage()) .set(AbstractArrow::setBaseDamage) .create(Keys.IS_CRITICAL_HIT) .get(AbstractArrow::isCritArrow) diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/AbstractChestedHorseData.java b/src/main/java/org/spongepowered/common/data/provider/entity/AbstractChestedHorseData.java index 58958516445..ee091a26e54 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/AbstractChestedHorseData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/AbstractChestedHorseData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.horse.AbstractChestedHorse; +import net.minecraft.world.entity.animal.equine.AbstractChestedHorse; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/AbstractHorseData.java b/src/main/java/org/spongepowered/common/data/provider/entity/AbstractHorseData.java index 06ba09e4c28..96b72955d2f 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/AbstractHorseData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/AbstractHorseData.java @@ -24,8 +24,10 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.horse.AbstractHorse; +import net.minecraft.world.entity.EntityReference; +import net.minecraft.world.entity.animal.equine.AbstractHorse; import org.spongepowered.api.data.Keys; +import org.spongepowered.common.accessor.world.entity.animal.equine.AbstractHorseAccessor; import org.spongepowered.common.bridge.world.entity.animal.horse.AbstractHorseBridge; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -42,10 +44,18 @@ public static void register(final DataProviderRegistrator registrator) { .get(AbstractHorse::isTamed) .set(AbstractHorse::setTamed) .create(Keys.OWNER) - .get(AbstractHorse::getOwnerUUID) + .get(h -> { + final var owner = h.getOwnerReference(); + if (owner == null) { + return null; + } + return owner.getUUID(); + }) .set((h, v) -> { - h.setOwnerUUID(v); - h.setTamed(v != null); + ((AbstractHorseAccessor) h).accessor$setOwner(EntityReference.of(v)); + }) + .delete(h -> { + h.setOwner(null); }) .asMutable(AbstractHorseBridge.class) .create(Keys.IS_SADDLED) diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/AbstractMinecartData.java b/src/main/java/org/spongepowered/common/data/provider/entity/AbstractMinecartData.java index 6d9e9883400..64d14454958 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/AbstractMinecartData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/AbstractMinecartData.java @@ -26,17 +26,20 @@ import net.minecraft.core.BlockPos; import net.minecraft.tags.BlockTags; -import net.minecraft.world.entity.vehicle.AbstractMinecart; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecart; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.persistence.DataContentUpdater; +import org.spongepowered.common.accessor.entity.vehicle.minecart.AbstractMinecartAccessor; import org.spongepowered.common.bridge.world.entity.vehicle.AbstractMinecartBridge; import org.spongepowered.common.data.ByteToBooleanContentUpdater; import org.spongepowered.common.data.SpongeDataManager; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.util.Constants; +import java.util.Optional; + public final class AbstractMinecartData { private AbstractMinecartData() { @@ -49,9 +52,9 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(AbstractMinecart.class) .create(Keys.BLOCK_STATE) - .get(h -> h.hasCustomDisplay() ? (BlockState) h.getDisplayBlockState() : null) - .set((h, v) -> h.setDisplayBlockState((net.minecraft.world.level.block.state.BlockState) v)) - .delete(h -> h.setCustomDisplay(false)) + .get(h -> (BlockState) h.getDisplayBlockState()) + .set((h, v) -> h.setCustomDisplayBlockState(Optional.ofNullable((net.minecraft.world.level.block.state.BlockState) v))) + .delete(h -> h.setCustomDisplayBlockState(Optional.empty())) .create(Keys.IS_ON_RAIL) .get(h -> { final BlockPos pos = h.blockPosition(); @@ -90,7 +93,7 @@ public static void register(final DataProviderRegistrator registrator) { // @formatter:on private static boolean setBlockOffset(final AbstractMinecart holder, final Integer value) { - if (!holder.hasCustomDisplay()) { + if (((AbstractMinecartAccessor) holder).accessor$getCustomDisplayBlockState().isEmpty()) { return false; } holder.setDisplayOffset(value); diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/AbstractVillagerData.java b/src/main/java/org/spongepowered/common/data/provider/entity/AbstractVillagerData.java index 52735629ee3..2befbf97419 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/AbstractVillagerData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/AbstractVillagerData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.npc.AbstractVillager; +import net.minecraft.world.entity.npc.villager.AbstractVillager; import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.common.data.provider.DataProviderRegistrator; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/AnimalData.java b/src/main/java/org/spongepowered/common/data/provider/entity/AnimalData.java index 23bc7a9e74d..d086ec3b5af 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/AnimalData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/AnimalData.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; +import net.minecraft.world.entity.EntityReference; import org.spongepowered.api.data.Keys; import org.spongepowered.common.accessor.world.entity.animal.AnimalAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -38,8 +39,8 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(AnimalAccessor.class) .create(Keys.BREEDER) - .get(AnimalAccessor::accessor$loveCause) - .set(AnimalAccessor::accessor$loveCause); + .get(a -> a.accessor$loveCause().getUUID()) + .set(((animalAccessor, uuid) -> animalAccessor.accessor$loveCause(EntityReference.of(uuid)))); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/AreaEffectCloudData.java b/src/main/java/org/spongepowered/common/data/provider/entity/AreaEffectCloudData.java index a62aab53c6a..d465f36a1fc 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/AreaEffectCloudData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/AreaEffectCloudData.java @@ -50,7 +50,7 @@ public static void register(final DataProviderRegistrator registrator) { .get(h -> Color.ofRgb(((AreaEffectCloudAccessor) h).accessor$potionContents().getColor())) .set((h, v) -> { var contents = ((AreaEffectCloudAccessor) h).accessor$potionContents(); - h.setPotionContents(new PotionContents(contents.potion(), Optional.of(v.rgb()), contents.customEffects())); + h.setPotionContents(new PotionContents(contents.potion(), Optional.of(v.rgb()), contents.customEffects(), contents.customName())); }) .create(Keys.DURATION) .get(x -> new SpongeTicks(x.getDuration())) @@ -64,7 +64,7 @@ public static void register(final DataProviderRegistrator registrator) { }) .create(Keys.PARTICLE_EFFECT) .get(h -> SpongeParticleHelper.spongeParticleOptions(h.getParticle())) - .set((h, v) -> h.setParticle(SpongeParticleHelper.vanillaParticleOptions(v))) + .set((h, v) -> h.setCustomParticle(SpongeParticleHelper.vanillaParticleOptions(v))) .create(Keys.RADIUS) .get(h -> (double) h.getRadius()) .set((h, v) -> h.setRadius(v.floatValue())) @@ -97,7 +97,7 @@ public static void register(final DataProviderRegistrator registrator) { .get(h -> PotionEffectUtil.copyAsPotionEffects(Streams.stream(h.accessor$potionContents().getAllEffects()).toList())) .set((h, v) -> { final PotionContents contents = h.accessor$potionContents(); - ((AreaEffectCloud) h).setPotionContents(new PotionContents(contents.potion(), contents.customColor(), PotionEffectUtil.copyAsEffectInstances(v))); + ((AreaEffectCloud) h).setPotionContents(new PotionContents(contents.potion(), contents.customColor(), PotionEffectUtil.copyAsEffectInstances(v), contents.customName())); }) .create(Keys.REAPPLICATION_DELAY) .get(h -> new SpongeTicks(h.accessor$reapplicationDelay())) diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/ArmorStandData.java b/src/main/java/org/spongepowered/common/data/provider/entity/ArmorStandData.java index 2398a2d8aa3..7194d88b880 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/ArmorStandData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/ArmorStandData.java @@ -60,10 +60,10 @@ public static void register(final DataProviderRegistrator registrator) { final Map values = new HashMap<>(); values.put(BodyParts.HEAD.get(), VecHelper.toVector3d(h.getHeadPose())); values.put(BodyParts.CHEST.get(), VecHelper.toVector3d(h.getBodyPose())); - values.put(BodyParts.LEFT_ARM.get(), VecHelper.toVector3d(((ArmorStandAccessor) h).accessor$leftArmPose())); - values.put(BodyParts.RIGHT_ARM.get(), VecHelper.toVector3d(((ArmorStandAccessor) h).accessor$rightArmPose())); - values.put(BodyParts.LEFT_LEG.get(), VecHelper.toVector3d(((ArmorStandAccessor) h).accessor$leftLegPose())); - values.put(BodyParts.RIGHT_LEG.get(), VecHelper.toVector3d(((ArmorStandAccessor) h).accessor$rightLegPose())); + values.put(BodyParts.LEFT_ARM.get(), VecHelper.toVector3d(h.getLeftArmPose())); + values.put(BodyParts.RIGHT_ARM.get(), VecHelper.toVector3d(h.getRightArmPose())); + values.put(BodyParts.LEFT_LEG.get(), VecHelper.toVector3d(h.getLeftLegPose())); + values.put(BodyParts.RIGHT_LEG.get(), VecHelper.toVector3d(h.getRightLegPose())); return values; }) .set((h, v) -> { @@ -78,11 +78,11 @@ public static void register(final DataProviderRegistrator registrator) { .get(h -> VecHelper.toVector3d(h.getBodyPose())) .set((h, v) -> h.setBodyPose(VecHelper.toRotation(v))) .create(Keys.HAS_ARMS) - .get(ArmorStand::isShowArms) - .set((h, v) -> ((ArmorStandAccessor) h).invoker$setShowArms(v)) + .get(ArmorStand::showArms) + .set(ArmorStand::setShowArms) .create(Keys.HAS_BASE_PLATE) - .get(h -> !h.isNoBasePlate()) - .set((h, v) -> ((ArmorStandAccessor) h).invoker$setNoBasePlate(!v)) + .get(ArmorStand::showBasePlate) + .set((h, v) -> h.setNoBasePlate(!v)) .create(Keys.HAS_MARKER) .get(ArmorStand::isMarker) .set((h, v) -> { @@ -167,16 +167,16 @@ public static void register(final DataProviderRegistrator registrator) { ((ArmorStandAccessor) h).accessor$disabledSlots(disabledSlots); }) .create(Keys.LEFT_ARM_ROTATION) - .get(h -> VecHelper.toVector3d(((ArmorStandAccessor) h).accessor$leftArmPose())) + .get(h -> VecHelper.toVector3d(h.getLeftArmPose())) .set((h, v) -> h.setLeftArmPose(VecHelper.toRotation(v))) .create(Keys.LEFT_LEG_ROTATION) - .get(h -> VecHelper.toVector3d(((ArmorStandAccessor) h).accessor$leftLegPose())) + .get(h -> VecHelper.toVector3d(h.getLeftLegPose())) .set((h, v) -> h.setLeftLegPose(VecHelper.toRotation(v))) .create(Keys.RIGHT_ARM_ROTATION) - .get(h -> VecHelper.toVector3d(((ArmorStandAccessor) h).accessor$rightArmPose())) + .get(h -> VecHelper.toVector3d(h.getRightArmPose())) .set((h, v) -> h.setRightArmPose(VecHelper.toRotation(v))) .create(Keys.RIGHT_LEG_ROTATION) - .get(h -> VecHelper.toVector3d(((ArmorStandAccessor) h).accessor$rightLegPose())) + .get(h -> VecHelper.toVector3d(h.getRightLegPose())) .set((h, v) -> h.setRightLegPose(VecHelper.toRotation(v))); } // @formatter:on diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/ArrowData.java b/src/main/java/org/spongepowered/common/data/provider/entity/ArrowData.java index b2f6a9a0769..f16ecd6df2e 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/ArrowData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/ArrowData.java @@ -25,13 +25,13 @@ package org.spongepowered.common.data.provider.entity; import net.minecraft.world.effect.MobEffectInstance; -import net.minecraft.world.entity.projectile.Arrow; +import net.minecraft.world.entity.projectile.arrow.Arrow; import net.minecraft.world.item.alchemy.PotionContents; import org.apache.commons.lang3.stream.Streams; import org.jetbrains.annotations.NotNull; import org.spongepowered.api.data.Keys; import org.spongepowered.api.effect.potion.PotionEffect; -import org.spongepowered.common.accessor.world.entity.projectile.ArrowAccessor; +import org.spongepowered.common.accessor.world.entity.projectile.arrow.ArrowAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; import java.util.List; @@ -56,7 +56,7 @@ public static void register(final DataProviderRegistrator registrator) { .set((h, v) -> { final PotionContents previousContents = ((ArrowAccessor) h).invoker$getPotionContents(); final List list = v.stream().map(effect -> ArrowData.clone((MobEffectInstance) effect)).toList(); - ((ArrowAccessor) h).invoker$setPotionContents(new PotionContents(previousContents.potion(), previousContents.customColor(), list)); + ((ArrowAccessor) h).invoker$setPotionContents(new PotionContents(previousContents.potion(), previousContents.customColor(), list, previousContents.customName())); }); } // @formatter:on diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/AxolotlData.java b/src/main/java/org/spongepowered/common/data/provider/entity/AxolotlData.java new file mode 100644 index 00000000000..3b2f4cc8003 --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/provider/entity/AxolotlData.java @@ -0,0 +1,41 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.provider.entity; + +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import org.spongepowered.api.data.Keys; +import org.spongepowered.common.data.provider.DataProviderRegistrator; + +public class AxolotlData { + private AxolotlData() { + } + + // @formatter:off + public static void register(final DataProviderRegistrator registrator) { + EntityDataProviders.enumOf(Keys.AXOLOTL_VARIANT, DataComponents.AXOLOTL_VARIANT).applyToRegistrator(registrator.asMutable(Axolotl.class)); + } + // @formatter:on +} diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/BoatData.java b/src/main/java/org/spongepowered/common/data/provider/entity/BoatData.java index 8fd0878b0fb..df9d50fb6a9 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/BoatData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/BoatData.java @@ -24,13 +24,13 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.entity.vehicle.boat.AbstractBoat; +import net.minecraft.world.entity.vehicle.boat.Boat; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.persistence.DataContentUpdater; -import org.spongepowered.api.data.type.BoatType; -import org.spongepowered.common.accessor.world.entity.vehicle.BoatAccessor; -import org.spongepowered.common.bridge.world.entity.vehicle.BoatBridge; +import org.spongepowered.common.accessor.world.entity.vehicle.boat.AbstractBoatAccessor; +import org.spongepowered.common.bridge.world.entity.vehicle.AbstractBoatBridge; import org.spongepowered.common.data.ByteToBooleanContentUpdater; import org.spongepowered.common.data.SpongeDataManager; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -46,32 +46,29 @@ private BoatData() { // @formatter:off public static void register(final DataProviderRegistrator registrator) { registrator - .asMutable(Boat.class) - .create(Keys.BOAT_TYPE) - .get(h -> ((BoatType) (Object) h.getVariant())) - .set((h, v) -> h.setVariant((Boat.Type) (Object) v)) - .asMutable(BoatAccessor.class) + .asMutable(AbstractBoat.class) + .asMutable(AbstractBoatAccessor.class) .create(Keys.IS_IN_WATER) .get(h -> h.accessor$status() == Boat.Status.IN_WATER) - .asMutable(BoatBridge.class) + .asMutable(AbstractBoatBridge.class) .create(Keys.CAN_MOVE_ON_LAND) - .get(BoatBridge::bridge$getMoveOnLand) - .set(BoatBridge::bridge$setMoveOnLand) - .asMutable(BoatBridge.class) + .get(AbstractBoatBridge::bridge$getMoveOnLand) + .set(AbstractBoatBridge::bridge$setMoveOnLand) + .asMutable(AbstractBoatBridge.class) .create(Keys.OCCUPIED_DECELERATION) - .get(BoatBridge::bridge$getOccupiedDecelerationSpeed) - .set(BoatBridge::bridge$setOccupiedDecelerationSpeed) - .asMutable(BoatBridge.class) + .get(AbstractBoatBridge::bridge$getOccupiedDecelerationSpeed) + .set(AbstractBoatBridge::bridge$setOccupiedDecelerationSpeed) + .asMutable(AbstractBoatBridge.class) .create(Keys.MAX_SPEED) - .get(BoatBridge::bridge$getMaxSpeed) - .set(BoatBridge::bridge$setMaxSpeed) - .asMutable(BoatBridge.class) + .get(AbstractBoatBridge::bridge$getMaxSpeed) + .set(AbstractBoatBridge::bridge$setMaxSpeed) + .asMutable(AbstractBoatBridge.class) .create(Keys.UNOCCUPIED_DECELERATION) - .get(BoatBridge::bridge$getUnoccupiedDecelerationSpeed) - .set(BoatBridge::bridge$setUnoccupiedDecelerationSpeed) + .get(AbstractBoatBridge::bridge$getUnoccupiedDecelerationSpeed) + .set(AbstractBoatBridge::bridge$setUnoccupiedDecelerationSpeed) ; final ResourceKey boatDataStoreKey = ResourceKey.sponge("boat"); - registrator.spongeDataStore(boatDataStoreKey, 2, new DataContentUpdater[]{BoatData.BOAT_UPDATER_BYTE_TO_BOOL_FIX}, BoatBridge.class, + registrator.spongeDataStore(boatDataStoreKey, 2, new DataContentUpdater[]{BoatData.BOAT_UPDATER_BYTE_TO_BOOL_FIX}, AbstractBoatBridge.class, Keys.MAX_SPEED, Keys.CAN_MOVE_ON_LAND, Keys.OCCUPIED_DECELERATION, Keys.UNOCCUPIED_DECELERATION); SpongeDataManager.INSTANCE.registerLegacySpongeData(Constants.Entity.Boat.BOAT_MAX_SPEED, boatDataStoreKey, Keys.MAX_SPEED); SpongeDataManager.INSTANCE.registerLegacySpongeData(Constants.Entity.Boat.BOAT_MOVE_ON_LAND, boatDataStoreKey, Keys.CAN_MOVE_ON_LAND); diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/CatData.java b/src/main/java/org/spongepowered/common/data/provider/entity/CatData.java index e5b33daf25b..cac638ca49b 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/CatData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/CatData.java @@ -25,12 +25,12 @@ package org.spongepowered.common.data.provider.entity; import net.minecraft.core.Holder; -import net.minecraft.world.entity.animal.Cat; -import net.minecraft.world.entity.animal.CatVariant; +import net.minecraft.world.entity.animal.feline.Cat; +import net.minecraft.world.entity.animal.feline.CatVariant; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.CatType; import org.spongepowered.api.data.type.DyeColor; -import org.spongepowered.common.accessor.world.entity.animal.CatAccessor; +import org.spongepowered.common.accessor.world.entity.animal.feline.CatAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.util.MissingImplementationException; @@ -45,7 +45,7 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(Cat.class) .create(Keys.CAT_TYPE) .get(h -> (CatType) (Object) h.getVariant().value()) - .set((h, v) -> h.setVariant(Holder.direct((CatVariant) (Object) v))) + .set((h, v) -> ((CatAccessor) h).invoker$setVariant(Holder.direct((CatVariant) (Object) v))) .create(Keys.DYE_COLOR) .get(h -> (DyeColor) (Object) h.getCollarColor()) .set((h, v) -> ((CatAccessor)h).invoker$setCollarColor((net.minecraft.world.item.DyeColor) (Object) v)) @@ -67,9 +67,7 @@ public static void register(final DataProviderRegistrator registrator) { .get(Cat::isLying) .set(Cat::setLying) .create(Keys.IS_PURRING) - .get(h -> { - throw new MissingImplementationException("CatData", "IS_PURRING::getter"); - }) + .get(h -> h.isLying() || ((CatAccessor) h).invoker$isRelaxStateOne()) .set((h, v) -> { throw new MissingImplementationException("CatData", "IS_PURRING::setter"); }) diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/ChickenData.java b/src/main/java/org/spongepowered/common/data/provider/entity/ChickenData.java index 04e8dfd8bae..5289fbef991 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/ChickenData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/ChickenData.java @@ -24,8 +24,10 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.Chicken; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.animal.chicken.Chicken; import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.type.ChickenVariant; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.util.SpongeTicks; @@ -47,6 +49,13 @@ public static void register(final DataProviderRegistrator registrator) { } h.eggTime = ticks; return true; + }) + .create(Keys.CHICKEN_VARIANT) + .get(h -> (ChickenVariant) (Object) h.getVariant().value()) + .set((h, v) -> { + final var holder = h.level().registryAccess().lookupOrThrow(Registries.CHICKEN_VARIANT) + .wrapAsHolder((net.minecraft.world.entity.animal.chicken.ChickenVariant) (Object) v); + h.setVariant(holder); }); } // @formatter:on diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/CommandBlockMinecartData.java b/src/main/java/org/spongepowered/common/data/provider/entity/CommandBlockMinecartData.java index ee00c87f8a6..1065855f7e8 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/CommandBlockMinecartData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/CommandBlockMinecartData.java @@ -25,7 +25,7 @@ package org.spongepowered.common.data.provider.entity; import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.vehicle.MinecartCommandBlock; +import net.minecraft.world.entity.vehicle.minecart.MinecartCommandBlock; import org.spongepowered.api.data.Keys; import org.spongepowered.common.accessor.world.level.BaseCommandBlockAccessor; import org.spongepowered.common.adventure.SpongeAdventure; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/CowData.java b/src/main/java/org/spongepowered/common/data/provider/entity/CowData.java new file mode 100644 index 00000000000..017f734d8b9 --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/provider/entity/CowData.java @@ -0,0 +1,51 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.provider.entity; + +import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.animal.cow.Cow; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.type.CowVariant; +import org.spongepowered.common.data.provider.DataProviderRegistrator; + +public final class CowData { + + private CowData() { + } + + // @formatter:off + public static void register(final DataProviderRegistrator registrator) { + registrator + .asMutable(Cow.class) + .create(Keys.COW_VARIANT) + .get(h -> (CowVariant) (Object) h.getVariant().value()) + .set((h, v) -> { + final var holder = h.level().registryAccess().lookupOrThrow(Registries.COW_VARIANT) + .wrapAsHolder((net.minecraft.world.entity.animal.cow.CowVariant) (Object) v); + h.setVariant(holder); + }); + } + // @formatter:on +} diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/CreakingData.java b/src/main/java/org/spongepowered/common/data/provider/entity/CreakingData.java new file mode 100644 index 00000000000..e1717027b15 --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/provider/entity/CreakingData.java @@ -0,0 +1,48 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.provider.entity; + +import net.minecraft.world.entity.monster.creaking.Creaking; +import org.spongepowered.api.data.Keys; +import org.spongepowered.common.data.provider.DataProviderRegistrator; +import org.spongepowered.common.util.VecHelper; + +public final class CreakingData { + + // @formatter:off + public static void register(final DataProviderRegistrator registrator) { + registrator + .asImmutable(Creaking.class) + .create(Keys.CREAKING_IS_LINKED) + .get(Creaking::isHeartBound); + registrator + .asMutable(Creaking.class) + .create(Keys.CREAKING_HOME_POSITION) + .get(h -> VecHelper.toVector3i(h.getHomePos())) + .set((h, v) -> h.setTransient(VecHelper.toBlockPos(v))); + ; + } + // @formatter:on +} diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/DamagingProjectileData.java b/src/main/java/org/spongepowered/common/data/provider/entity/DamagingProjectileData.java index 637e43bf59b..3ee14260859 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/DamagingProjectileData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/DamagingProjectileData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.projectile.AbstractHurtingProjectile; +import net.minecraft.world.entity.projectile.hurtingprojectile.AbstractHurtingProjectile; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/DolphinData.java b/src/main/java/org/spongepowered/common/data/provider/entity/DolphinData.java index be838cdebb3..4a5aa9d00a2 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/DolphinData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/DolphinData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.Dolphin; +import net.minecraft.world.entity.animal.dolphin.Dolphin; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/EnderCrystalData.java b/src/main/java/org/spongepowered/common/data/provider/entity/EnderCrystalData.java index 44fa3a18e24..8cf97d1aa6b 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/EnderCrystalData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/EnderCrystalData.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.boss.enderdragon.EndCrystal; import org.spongepowered.api.data.Keys; @@ -50,7 +51,11 @@ public static void register(final DataProviderRegistrator registrator) { return false; } if (v == 0) { - h.hurt((DamageSource) SpongeDamageSources.IGNORED, 1000F); + final var damageSource = (DamageSource) SpongeDamageSources.IGNORED; + switch (h.level()) { + case ServerLevel sl -> h.hurtServer(sl, damageSource, 1000F); + default -> h.hurtClient(damageSource); + } } else { ((EntityAccessor) h).invoker$unsetRemoved(); } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/EnderDragonData.java b/src/main/java/org/spongepowered/common/data/provider/entity/EnderDragonData.java index 1e7e9210906..7f8155cd4ef 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/EnderDragonData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/EnderDragonData.java @@ -25,11 +25,11 @@ package org.spongepowered.common.data.provider.entity; import net.minecraft.world.entity.boss.enderdragon.EnderDragon; -import net.minecraft.world.level.dimension.end.EndDragonFight; +import net.minecraft.world.level.dimension.end.EnderDragonFight; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.explosive.EndCrystal; -import org.spongepowered.common.accessor.world.level.dimension.end.EndDragonFightAccessor; +import org.spongepowered.common.accessor.world.level.dimension.end.EnderDragonFightAccessor; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -47,9 +47,9 @@ public static void register(final DataProviderRegistrator registrator) { .set((h, v) -> h.nearestCrystal = (net.minecraft.world.entity.boss.enderdragon.EndCrystal) v) .create(Keys.BOSS_BAR) .get(h -> { - final @Nullable EndDragonFight fight = h.getDragonFight(); + final @Nullable EnderDragonFight fight = h.getDragonFight(); if (fight != null) { - return SpongeAdventure.asAdventure(((EndDragonFightAccessor) fight).accessor$dragonEvent()); + return SpongeAdventure.asAdventure(((EnderDragonFightAccessor) fight).accessor$dragonEvent()); } else { return null; } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/EntityData.java b/src/main/java/org/spongepowered/common/data/provider/entity/EntityData.java index 69937fac579..9ab1a6275fb 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/EntityData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/EntityData.java @@ -170,8 +170,8 @@ public static void register(final DataProviderRegistrator registrator) { .get(Entity::isCustomNameVisible) .set(Entity::setCustomNameVisible) .create(Keys.IS_FLYING) - .get(h -> h.hasImpulse) - .set((h, v) -> h.hasImpulse = v) + .get(h -> h.needsSync) + .set((h, v) -> h.needsSync = v) .supports(h -> !(h instanceof Player)) .create(Keys.IS_GLOWING) .get(Entity::hasGlowingTag) @@ -197,7 +197,7 @@ public static void register(final DataProviderRegistrator registrator) { .delete(Entity::ejectPassengers) .set((h, v) -> { h.ejectPassengers(); - v.forEach(v1 -> ((Entity) v1).startRiding(h, true)); + v.forEach(v1 -> ((Entity) v1).startRiding(h, true, true)); }) .create(Keys.PUSH_REACTION) .get(h -> (PushReaction) (Object) h.getPistonPushReaction()) @@ -216,15 +216,15 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.SCALE) .get(h -> 1d) .create(Keys.SCOREBOARD_TAGS) - .get(Entity::getTags) - .delete(h -> h.getTags().clear()) + .get(Entity::entityTags) + .delete(h -> h.entityTags().clear()) .set((h, v) -> { - h.getTags().clear(); - h.getTags().addAll(v); + h.entityTags().clear(); + h.entityTags().addAll(v); }) .create(Keys.VEHICLE) .get(h -> (org.spongepowered.api.entity.Entity) h.getVehicle()) - .set((h, v) -> h.startRiding((Entity) v, true)) + .set((h, v) -> h.startRiding((Entity) v, true, true)) .delete(h -> h.stopRiding()) .create(Keys.VELOCITY) .get(h -> VecHelper.toVector3d(h.getDeltaMovement())) diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/EntityDataProviders.java b/src/main/java/org/spongepowered/common/data/provider/entity/EntityDataProviders.java index 39a1ea8b74a..397f528d03d 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/EntityDataProviders.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/EntityDataProviders.java @@ -24,10 +24,77 @@ */ package org.spongepowered.common.data.provider.entity; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.entity.Entity; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.registry.DefaultedRegistryValue; +import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.data.provider.DataProviderRegistratorBuilder; +import java.util.List; + public final class EntityDataProviders extends DataProviderRegistratorBuilder { + public sealed interface KeyComponentProvider { + void applyToRegistrator(DataProviderRegistrator.MutableRegistrator registrator); + } + + public static List> of(KeyComponentProvider... providers) { + return List.of(providers); + } + + public static KeyComponentProvider holderOf( + Key> apiKey, + DataComponentType> componentType, + ResourceKey> resourceKey + ) { + return new HolderProvider<>(apiKey, componentType, resourceKey); + } + + public static > KeyComponentProvider enumOf( + Key> apiKey, + DataComponentType componentType + ) { + return new EnumProvider<>(apiKey, componentType); + } + + @SuppressWarnings("unchecked") + record HolderProvider( + Key> apiKey, + DataComponentType> componentType, + ResourceKey> resourceKey + ) implements KeyComponentProvider { + public void applyToRegistrator(DataProviderRegistrator.MutableRegistrator registrator) { + registrator.create(this.apiKey) + .get(h -> (A) h.get(this.componentType).value()) + .setAnd((h, v) -> { + final var holder = h.level().registryAccess().lookup(this.resourceKey) + .map(r -> r.wrapAsHolder((MC) (Object) v)); + if (holder.isEmpty()) { + return false; + } + h.setComponent(this.componentType, holder.get()); + return true; + }); + } + } + + @SuppressWarnings("unchecked") + record EnumProvider>( + Key> apiKey, + DataComponentType componentType + ) implements KeyComponentProvider { + public void applyToRegistrator(DataProviderRegistrator.MutableRegistrator registrator) { + registrator.create(this.apiKey) + .get(h -> (A) h.get(this.componentType)) + .set((h, v) -> h.setComponent(this.componentType, (MC) (Object) v)); + } + } + @Override public void registerProviders() { AbstractArrowData.register(this.registrator); @@ -42,6 +109,7 @@ public void registerProviders() { AreaEffectCloudData.register(this.registrator); ArmorStandData.register(this.registrator); ArrowData.register(this.registrator); + AxolotlData.register(this.registrator); BatData.register(this.registrator); BedData.register(this.registrator); BlazeData.register(this.registrator); @@ -49,6 +117,8 @@ public void registerProviders() { CatData.register(this.registrator); ChickenData.register(this.registrator); CommandBlockMinecartData.register(this.registrator); + CowData.register(this.registrator); + CreakingData.register(this.registrator); CreeperData.register(this.registrator); DamagingProjectileData.register(this.registrator); DolphinData.register(this.registrator); @@ -85,6 +155,7 @@ public void registerProviders() { LivingData.register(this.registrator); LlamaData.register(this.registrator); LocationTargetingData.register(this.registrator); + MannequinData.register(this.registrator); MobData.register(this.registrator); MooshroomData.register(this.registrator); OcelotData.register(this.registrator); @@ -102,6 +173,7 @@ public void registerProviders() { PufferfishData.register(this.registrator); RabbitData.register(this.registrator); RavagerData.register(this.registrator); + SalmonData.register(this.registrator); ServerPlayerData.register(this.registrator); SheepData.register(this.registrator); ShulkerBulletData.register(this.registrator); @@ -109,6 +181,8 @@ public void registerProviders() { SlimeData.register(this.registrator); SpellcastingIllagerData.register(this.registrator); SpiderData.register(this.registrator); + StriderData.register(this.registrator); + TadpoleData.register(this.registrator); TameableData.register(this.registrator); ThrowableItemProjectileData.register(this.registrator); TNTData.register(this.registrator); diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/EvokerData.java b/src/main/java/org/spongepowered/common/data/provider/entity/EvokerData.java index bcdfecdf72c..050aa8e4cc6 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/EvokerData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/EvokerData.java @@ -26,7 +26,7 @@ import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.living.animal.Sheep; -import org.spongepowered.common.accessor.world.entity.monster.EvokerAccessor; +import org.spongepowered.common.accessor.world.entity.monster.illager.EvokerAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class EvokerData { @@ -40,7 +40,7 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(EvokerAccessor.class) .create(Keys.WOLOLO_TARGET) .get(h -> (Sheep) h.accessor$wololoTarget()) - .set((h, v) -> h.accessor$wololoTarget((net.minecraft.world.entity.animal.Sheep) v)); + .set((h, v) -> h.accessor$wololoTarget((net.minecraft.world.entity.animal.sheep.Sheep) v)); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/FoxData.java b/src/main/java/org/spongepowered/common/data/provider/entity/FoxData.java index f13dc6efb5e..163203e22ed 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/FoxData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/FoxData.java @@ -24,10 +24,11 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.Fox; +import net.minecraft.world.entity.EntityReference; +import net.minecraft.world.entity.animal.fox.Fox; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.FoxType; -import org.spongepowered.common.accessor.world.entity.animal.FoxAccessor; +import org.spongepowered.common.accessor.world.entity.animal.fox.FoxAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; import java.util.Optional; @@ -42,11 +43,11 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(Fox.class) .create(Keys.FIRST_TRUSTED) - .get(h -> h.getEntityData().get(FoxAccessor.accessor$DATA_TRUSTED_ID_0()).orElse(null)) - .set((h, v) -> h.getEntityData().set(FoxAccessor.accessor$DATA_TRUSTED_ID_0(), Optional.ofNullable(v))) + .get(h -> h.getEntityData().get(FoxAccessor.accessor$DATA_TRUSTED_ID_0()).map(EntityReference::getUUID).orElse(null)) + .set((h, v) -> h.getEntityData().set(FoxAccessor.accessor$DATA_TRUSTED_ID_0(), Optional.ofNullable(v).map(EntityReference::of))) .create(Keys.FOX_TYPE) .get(h -> (FoxType) (Object) h.getVariant()) - .set((h, v) -> h.setVariant((Fox.Type) (Object) v)) + .set((h, v) -> ((FoxAccessor) h ).invoker$setVariant((Fox.Variant) (Object) v)) .create(Keys.IS_CROUCHING) .get(Fox::isCrouching) .set(Fox::setIsCrouching) @@ -66,8 +67,8 @@ public static void register(final DataProviderRegistrator registrator) { .get(Fox::isSleeping) .set((h, v) -> ((FoxAccessor) h).invoker$setSleeping(v)) .create(Keys.SECOND_TRUSTED) - .get(h -> h.getEntityData().get(FoxAccessor.accessor$DATA_TRUSTED_ID_1()).orElse(null)) - .set((h, v) -> h.getEntityData().set(FoxAccessor.accessor$DATA_TRUSTED_ID_1(), Optional.ofNullable(v))); + .get(h -> h.getEntityData().get(FoxAccessor.accessor$DATA_TRUSTED_ID_1()).map(EntityReference::getUUID).orElse(null)) + .set((h, v) -> h.getEntityData().set(FoxAccessor.accessor$DATA_TRUSTED_ID_1(), Optional.ofNullable(v).map(EntityReference::of))); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/FrogData.java b/src/main/java/org/spongepowered/common/data/provider/entity/FrogData.java index 2fef31a0006..1b8b3a7d86a 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/FrogData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/FrogData.java @@ -24,11 +24,12 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.world.entity.animal.FrogVariant; +import net.minecraft.core.registries.Registries; import net.minecraft.world.entity.animal.frog.Frog; +import net.minecraft.world.entity.animal.frog.FrogVariant; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.FrogType; +import org.spongepowered.common.accessor.entity.animal.frog.FrogAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class FrogData { @@ -42,7 +43,12 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(Frog.class) .create(Keys.FROG_TYPE) .get(h -> (FrogType) (Object) h.getVariant().value()) - .set((h, v) -> h.setVariant(BuiltInRegistries.FROG_VARIANT.wrapAsHolder((FrogVariant) (Object) v))); + .set((h, v) -> { + final var holder = h.level().registryAccess().lookup(Registries.FROG_VARIANT) + .orElseThrow() + .wrapAsHolder(((FrogVariant) (Object) v)); + ((FrogAccessor) h).invoker$setVariant(holder); + }); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/FurnaceMinecartData.java b/src/main/java/org/spongepowered/common/data/provider/entity/FurnaceMinecartData.java index c0b85677851..dc257ecd559 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/FurnaceMinecartData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/FurnaceMinecartData.java @@ -25,7 +25,7 @@ package org.spongepowered.common.data.provider.entity; import org.spongepowered.api.data.Keys; -import org.spongepowered.common.accessor.world.entity.vehicle.MinecartFurnaceAccessor; +import org.spongepowered.common.accessor.world.entity.vehicle.minecart.MinecartFurnaceAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class FurnaceMinecartData { diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/HorseData.java b/src/main/java/org/spongepowered/common/data/provider/entity/HorseData.java index 2f40374ac79..38a8585dadb 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/HorseData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/HorseData.java @@ -25,13 +25,13 @@ package org.spongepowered.common.data.provider.entity; import net.minecraft.core.MappedRegistry; -import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.equine.Horse; import org.spongepowered.api.Sponge; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.HorseColor; import org.spongepowered.api.data.type.HorseStyle; import org.spongepowered.api.registry.RegistryTypes; -import org.spongepowered.common.accessor.world.entity.animal.horse.HorseAccessor; +import org.spongepowered.common.accessor.world.entity.animal.equine.HorseAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class HorseData { diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/IronGolemData.java b/src/main/java/org/spongepowered/common/data/provider/entity/IronGolemData.java index 53441f770ff..83f3a585789 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/IronGolemData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/IronGolemData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.IronGolem; +import net.minecraft.world.entity.animal.golem.IronGolem; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/LeashableData.java b/src/main/java/org/spongepowered/common/data/provider/entity/LeashableData.java index 1948a9b0db0..ec41edfbcd6 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/LeashableData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/LeashableData.java @@ -42,13 +42,10 @@ public static void register(final DataProviderRegistrator registrator) { final var ld = h.getLeashData(); return ld == null ? null : (Entity) ld.leashHolder; }) - .delete(h -> { - h.dropLeash(true, false); - }) + .delete(Leashable::removeLeash) .set((h, v) -> { if (v == null) { - // TODO remove - h.dropLeash(true, false); + h.removeLeash(); } else { h.setLeashedTo((net.minecraft.world.entity.Entity) v, true); } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/LivingData.java b/src/main/java/org/spongepowered/common/data/provider/entity/LivingData.java index 3e02fa952f6..bfd58682a50 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/LivingData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/LivingData.java @@ -25,6 +25,7 @@ package org.spongepowered.common.data.provider.entity; import com.google.common.collect.ImmutableMap; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.entity.LivingEntity; @@ -165,8 +166,11 @@ public static void register(final DataProviderRegistrator registrator) { } if (v == 0) { - // Cause DestructEntityEvent to fire first - h.hurt((DamageSource) SpongeDamageSources.IGNORED, Float.MAX_VALUE); + final var ignoredSource = (DamageSource) SpongeDamageSources.IGNORED; + switch (h.level()) { + case ServerLevel sl -> h.hurtServer(sl, ignoredSource, Float.MAX_VALUE); + default -> h.hurtClient(ignoredSource); + } } h.setHealth(v.floatValue()); return true; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/LlamaData.java b/src/main/java/org/spongepowered/common/data/provider/entity/LlamaData.java index c69b7e79fbf..21245664a2d 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/LlamaData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/LlamaData.java @@ -24,10 +24,10 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.horse.Llama; +import net.minecraft.world.entity.animal.equine.Llama; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.LlamaType; -import org.spongepowered.common.accessor.world.entity.animal.horse.LlamaAccessor; +import org.spongepowered.common.accessor.world.entity.animal.equine.LlamaAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class LlamaData { @@ -45,7 +45,7 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(Llama.class) .create(Keys.LLAMA_TYPE) .get(h -> (LlamaType) (Object) h.getVariant()) - .set((h, v) -> h.setVariant((Llama.Variant) (Object) v)); + .set((h, v) -> ((LlamaAccessor) h).invoker$setVariant((Llama.Variant) (Object) v)); } // @formatter:on } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/advancement/DisplayInfo_BuilderMixin_ItemStackLike.java b/src/main/java/org/spongepowered/common/data/provider/entity/MannequinData.java similarity index 64% rename from src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/advancement/DisplayInfo_BuilderMixin_ItemStackLike.java rename to src/main/java/org/spongepowered/common/data/provider/entity/MannequinData.java index e752998a343..0f63c58994f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/advancement/DisplayInfo_BuilderMixin_ItemStackLike.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/MannequinData.java @@ -22,21 +22,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.itemstacklike.advancement; +package org.spongepowered.common.data.provider.entity; -import org.spongepowered.api.advancement.DisplayInfo; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.asm.mixin.Mixin; +import net.minecraft.world.entity.HumanoidArm; +import net.minecraft.world.entity.decoration.Mannequin; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.type.HandPreference; +import org.spongepowered.common.data.provider.DataProviderRegistrator; -@Mixin(value = DisplayInfo.Builder.class, remap = false) -public interface DisplayInfo_BuilderMixin_ItemStackLike { - - default DisplayInfo.Builder icon(ItemStack itemStack) { - return ((DisplayInfo.Builder) this).icon(itemStack); +public final class MannequinData { + private MannequinData() { } - default DisplayInfo.Builder icon(ItemStackSnapshot itemStackSnapshot) { - return ((DisplayInfo.Builder) this).icon(itemStackSnapshot); + public static void register(DataProviderRegistrator registrator) { + registrator + .asMutable(Mannequin.class) + .create(Keys.DOMINANT_HAND) + .get(h -> (HandPreference) (Object) h.getMainArm()) + .set((h, v) -> h.setMainArm((HumanoidArm) (Object) v)) + ; } } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/MooshroomData.java b/src/main/java/org/spongepowered/common/data/provider/entity/MooshroomData.java index 73587f66a71..e9cb7e00fa0 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/MooshroomData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/MooshroomData.java @@ -24,9 +24,10 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.MushroomCow; +import net.minecraft.world.entity.animal.cow.MushroomCow; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.MooshroomType; +import org.spongepowered.common.accessor.world.entity.animal.cow.MooshroomAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class MooshroomData { @@ -40,7 +41,7 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(MushroomCow.class) .create(Keys.MOOSHROOM_TYPE) .get(h -> (MooshroomType) (Object) h.getVariant()) - .set((h, v) -> h.setVariant((MushroomCow.MushroomType) (Object) v)); + .set((h, v) -> ((MooshroomAccessor) h).invoker$setVariant((MushroomCow.Variant) (Object) v)); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/OcelotData.java b/src/main/java/org/spongepowered/common/data/provider/entity/OcelotData.java index 636404e2d3a..361b5167a1f 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/OcelotData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/OcelotData.java @@ -25,7 +25,7 @@ package org.spongepowered.common.data.provider.entity; import org.spongepowered.api.data.Keys; -import org.spongepowered.common.accessor.world.entity.animal.OcelotAccessor; +import org.spongepowered.common.accessor.world.entity.animal.feline.OcelotAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class OcelotData { diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/PaintingData.java b/src/main/java/org/spongepowered/common/data/provider/entity/PaintingData.java index b5f7f253f77..e8e1fce95e8 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/PaintingData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/PaintingData.java @@ -29,14 +29,15 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerPlayerConnection; -import net.minecraft.world.entity.decoration.Painting; -import net.minecraft.world.entity.decoration.PaintingVariant; +import net.minecraft.world.entity.decoration.painting.Painting; +import net.minecraft.world.entity.decoration.painting.PaintingVariant; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.ArtType; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.accessor.server.level.ChunkMapAccessor; import org.spongepowered.common.accessor.server.level.ChunkMap_TrackedEntityAccessor; import org.spongepowered.common.accessor.world.entity.decoration.HangingEntityAccessor; +import org.spongepowered.common.accessor.world.entity.decoration.painting.PaintingAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class PaintingData { @@ -51,13 +52,13 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.ART_TYPE) .get(h -> (ArtType) (Object) h.getVariant().value()) .setAnd((h, v) -> { - if (!h.level().isClientSide) { + if (!h.level().isClientSide()) { final Holder oldArt = h.getVariant(); - var newArt = SpongeCommon.server().registryAccess().registryOrThrow(Registries.PAINTING_VARIANT).wrapAsHolder((PaintingVariant) (Object) v); - h.setVariant(newArt); + var newArt = SpongeCommon.vanillaRegistry(Registries.PAINTING_VARIANT).wrapAsHolder((PaintingVariant) (Object) v); + ((PaintingAccessor) h).invoker$setVariant(newArt); ((HangingEntityAccessor) h).invoker$setDirection(h.getDirection()); if (!h.survives()) { - h.setVariant(oldArt); + ((PaintingAccessor) h).invoker$setVariant(oldArt); ((HangingEntityAccessor) h).invoker$setDirection(h.getDirection()); return false; } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/PandaData.java b/src/main/java/org/spongepowered/common/data/provider/entity/PandaData.java index 108618c2361..ebc827147ff 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/PandaData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/PandaData.java @@ -24,10 +24,10 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.Panda; +import net.minecraft.world.entity.animal.panda.Panda; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.PandaGene; -import org.spongepowered.common.accessor.world.entity.animal.PandaAccessor; +import org.spongepowered.common.accessor.world.entity.animal.panda.PandaAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.util.SpongeTicks; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/ParrotData.java b/src/main/java/org/spongepowered/common/data/provider/entity/ParrotData.java index fc944968918..7c88008a879 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/ParrotData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/ParrotData.java @@ -24,9 +24,10 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.Parrot; +import net.minecraft.world.entity.animal.parrot.Parrot; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.ParrotType; +import org.spongepowered.common.accessor.world.entity.animal.parrot.ParrotAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class ParrotData { @@ -40,7 +41,7 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(Parrot.class) .create(Keys.PARROT_TYPE) .get(h -> (ParrotType) (Object) h.getVariant()) - .set((h, v) -> h.setVariant((Parrot.Variant) (Object) v)); + .set((h, v) -> ((ParrotAccessor) h).invoker$setVariant((Parrot.Variant) (Object) v)); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/PigData.java b/src/main/java/org/spongepowered/common/data/provider/entity/PigData.java index 310a184ba5a..dc013bbcf8c 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/PigData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/PigData.java @@ -24,9 +24,15 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.Pig; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.animal.pig.Pig; +import net.minecraft.world.entity.animal.pig.PigVariant; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import org.spongepowered.api.data.Keys; -import org.spongepowered.common.accessor.world.entity.animal.PigAccessor; +import org.spongepowered.api.data.type.PigType; +import org.spongepowered.common.accessor.world.entity.animal.pig.PigAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class PigData { @@ -42,11 +48,20 @@ public static void register(final DataProviderRegistrator registrator) { .get(Pig::isSaddled) .set((h, v) -> { if (v) { - h.equipSaddle(null, null); + h.setItemSlot(EquipmentSlot.SADDLE, new ItemStack(Items.SADDLE)); } else { - ((PigAccessor) h).accessor$steering().setSaddle(false); + h.setItemSlot(EquipmentSlot.SADDLE, ItemStack.EMPTY); } }) + .create(Keys.PIG_TYPE) + .get(p -> (PigType) (Object) p.getVariant().value()) + .setAnd((h, v) -> h.level().registryAccess().lookup(Registries.PIG_VARIANT) + .map(r -> { + var newPigType = r.wrapAsHolder((PigVariant) (Object) v); + ((PigAccessor) h).invoker$setVariant(newPigType); + return true; + }) + .orElse(false)) ; } // @formatter:on diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/PillagerData.java b/src/main/java/org/spongepowered/common/data/provider/entity/PillagerData.java index 63165c7b34d..8976d5740be 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/PillagerData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/PillagerData.java @@ -24,9 +24,9 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.monster.Pillager; +import net.minecraft.world.entity.monster.illager.Pillager; import org.spongepowered.api.data.Keys; -import org.spongepowered.common.accessor.world.entity.monster.PillagerAccessor; +import org.spongepowered.common.accessor.world.entity.monster.illager.PillagerAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class PillagerData { diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/PlayerData.java b/src/main/java/org/spongepowered/common/data/provider/entity/PlayerData.java index ba5b59f02f5..745bd8a8c1c 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/PlayerData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/PlayerData.java @@ -60,7 +60,7 @@ public static void register(final DataProviderRegistrator registrator) { .get(h -> (HandPreference) (Object) h.getMainArm()) .set((h, v) -> h.setMainArm((HumanoidArm) (Object) v)) .create(Keys.EXHAUSTION) - .get(h -> (double) h.getFoodData().getExhaustionLevel()) + .get(h -> (double) ((FoodDataAccessor) h.getFoodData()).accessor$exhaustionLevel()) .set((h, v) -> ((FoodDataAccessor) h.getFoodData()).accessor$exhaustionLevel(v.floatValue())) .create(Keys.EXPERIENCE) .get(h -> h.totalExperience) diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/PolarBearData.java b/src/main/java/org/spongepowered/common/data/provider/entity/PolarBearData.java index 3bdbffef841..095dc447729 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/PolarBearData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/PolarBearData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.PolarBear; +import net.minecraft.world.entity.animal.polarbear.PolarBear; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/PotionData.java b/src/main/java/org/spongepowered/common/data/provider/entity/PotionData.java index 124becfdac8..f0f35429966 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/PotionData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/PotionData.java @@ -26,7 +26,7 @@ import net.minecraft.core.component.DataComponents; import net.minecraft.world.effect.MobEffectInstance; -import net.minecraft.world.entity.projectile.ThrownPotion; +import net.minecraft.world.entity.projectile.throwableitemprojectile.AbstractThrownPotion; import net.minecraft.world.item.alchemy.PotionContents; import org.apache.commons.lang3.stream.Streams; import org.spongepowered.api.data.Keys; @@ -41,12 +41,12 @@ private PotionData() { // @formatter:off public static void register(final DataProviderRegistrator registrator) { registrator - .asMutable(ThrownPotion.class) + .asMutable(AbstractThrownPotion.class) .create(Keys.POTION_EFFECTS) .get(h -> Streams.of(h.getItem().getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY).getAllEffects()).map(PotionEffect.class::cast).toList()) .set((h, v) -> { final var mcEffects = v.stream().map(MobEffectInstance.class::cast).toList(); - h.getItem().update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(contents.potion(), contents.customColor(), mcEffects)); + h.getItem().update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(contents.potion(), contents.customColor(), mcEffects, contents.customName())); }); } // @formatter:on diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/PufferfishData.java b/src/main/java/org/spongepowered/common/data/provider/entity/PufferfishData.java index 6e62f127bf5..2fa89155b3b 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/PufferfishData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/PufferfishData.java @@ -24,9 +24,9 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.Pufferfish; +import net.minecraft.world.entity.animal.fish.Pufferfish; import org.spongepowered.api.data.Keys; -import org.spongepowered.common.accessor.world.entity.animal.PufferfishAccessor; +import org.spongepowered.common.accessor.world.entity.animal.fish.PufferfishAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class PufferfishData { @@ -39,7 +39,11 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(Pufferfish.class) .create(Keys.SCALE) - .get(h -> (double) PufferfishAccessor.invoker$getScale(h.getPuffState())); + .get(h -> (double) PufferfishAccessor.invoker$getScale(h.getPuffState())) + .create(Keys.PUFFER_FISH_PUFFINESS_STATE) + .get(Pufferfish::getPuffState) + .set(Pufferfish::setPuffState) + ; } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/RabbitData.java b/src/main/java/org/spongepowered/common/data/provider/entity/RabbitData.java index 0b46e9f5e36..54ee3619aca 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/RabbitData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/RabbitData.java @@ -24,9 +24,10 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.Rabbit; +import net.minecraft.world.entity.animal.rabbit.Rabbit; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.RabbitType; +import org.spongepowered.common.accessor.world.entity.animal.rabbit.RabbitAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class RabbitData { @@ -40,7 +41,7 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(Rabbit.class) .create(Keys.RABBIT_TYPE) .get(h -> (RabbitType) (Object) h.getVariant()) - .set((h, v) -> h.setVariant((Rabbit.Variant) (Object) v)); + .set((h, v) -> ((RabbitAccessor) h).invoker$setVariant((Rabbit.Variant) (Object) v)); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/SalmonData.java b/src/main/java/org/spongepowered/common/data/provider/entity/SalmonData.java new file mode 100644 index 00000000000..2e55974f395 --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/provider/entity/SalmonData.java @@ -0,0 +1,41 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.provider.entity; + +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.entity.animal.fish.Salmon; +import org.spongepowered.api.data.Keys; +import org.spongepowered.common.data.provider.DataProviderRegistrator; + +public final class SalmonData { + + private SalmonData() {} + + // @formatter:off + public static void register(final DataProviderRegistrator registrator) { + EntityDataProviders.enumOf(Keys.SALMON_SIZE, DataComponents.SALMON_SIZE).applyToRegistrator(registrator.asMutable(Salmon.class)); + } + // @formatter:on +} diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/ServerPlayerData.java b/src/main/java/org/spongepowered/common/data/provider/entity/ServerPlayerData.java index 61c63ce3516..86d6fedc4bd 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/ServerPlayerData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/ServerPlayerData.java @@ -24,7 +24,10 @@ */ package org.spongepowered.common.data.provider.entity; +import com.google.common.collect.HashMultimap; +import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket; import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; @@ -32,13 +35,11 @@ import net.minecraft.network.protocol.game.ClientboundRespawnPacket; import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket; import net.minecraft.network.protocol.game.ClientboundUpdateMobEffectPacket; -import net.minecraft.network.protocol.game.CommonPlayerSpawnInfo; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.players.PlayerList; import net.minecraft.stats.Stat; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.level.GameType; -import net.minecraft.world.level.biome.BiomeManager; import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.living.player.chat.ChatVisibilities; @@ -50,6 +51,7 @@ import org.spongepowered.common.accessor.stats.StatsCounterAccessor; import org.spongepowered.common.bridge.server.level.ServerPlayerBridge; import org.spongepowered.common.bridge.server.level.ServerPlayerEntityHealthScaleBridge; +import org.spongepowered.common.bridge.world.entity.player.PlayerBridge; import org.spongepowered.common.data.SpongeDataManager; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.profile.SpongeProfileProperty; @@ -76,14 +78,18 @@ public static void register(final DataProviderRegistrator registrator) { .get(h -> (GameMode) (Object) h.gameMode.getPreviousGameModeForPlayer()) .create(Keys.SKIN_PROFILE_PROPERTY) .get(h -> { - final Collection properties = h.getGameProfile().getProperties().get(ProfileProperty.TEXTURES); + final Collection properties = h.getGameProfile().properties().get(ProfileProperty.TEXTURES); if (properties.isEmpty()) { return null; } return new SpongeProfileProperty(properties.iterator().next()); }) .set((h ,v) -> { - h.getGameProfile().getProperties().replaceValues(ProfileProperty.TEXTURES, Collections.singletonList(((SpongeProfileProperty)v).asProperty())); + final var profile = h.getGameProfile(); + final var mutableProperties = HashMultimap.create(profile.properties()); + mutableProperties.replaceValues(ProfileProperty.TEXTURES, Collections.singletonList(((SpongeProfileProperty)v).asProperty())); + + ((PlayerBridge) h).bridge$setGameProfile(new GameProfile(profile.id(), profile.name(), new PropertyMap(mutableProperties))); ServerPlayerData.resendProfile(h); }) .create(Keys.SPECTATOR_TARGET) @@ -135,33 +141,25 @@ private static void resendProfile(final ServerPlayer h) { // should work without this on ServerSideConnectionEvent.Login if (h.level().getEntity(h.getId()) == h && !h.isRemoved()) { // Remove Entity - h.serverLevel().getChunkSource().removeEntity(h); + h.level().getChunkSource().removeEntity(h); // Remove from TabList - h.getServer().getPlayerList().broadcastAll(new ClientboundPlayerInfoRemovePacket(List.of(h.getUUID()))); + ((ServerPlayerAccessor) h).accessor$server().getPlayerList().broadcastAll(new ClientboundPlayerInfoRemovePacket(List.of(h.getUUID()))); // Add back to TabList - h.getServer().getPlayerList().broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(h))); + ((ServerPlayerAccessor) h).accessor$server().getPlayerList().broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(h))); // Add Entity - h.serverLevel().getChunkSource().addEntity(h); + h.level().getChunkSource().addEntity(h); // Reconnect local player - h.connection.send(new ClientboundRespawnPacket(new CommonPlayerSpawnInfo( - h.level().dimensionTypeRegistration(), - h.level().dimension(), - BiomeManager.obfuscateSeed(h.serverLevel().getSeed()), - h.gameMode.getGameModeForPlayer(), - h.gameMode.getPreviousGameModeForPlayer(), - h.serverLevel().isDebug(), - h.serverLevel().isFlat(), - h.getLastDeathLocation(), - h.getPortalCooldown()), (byte) 0)); + final var commonSpawnInfo = h.createCommonSpawnInfo(h.level()); + h.connection.send(new ClientboundRespawnPacket(commonSpawnInfo, (byte) 0)); // tp - just in case h.connection.teleport(h.getX(), h.getY(), h.getZ(), h.getYRot(), h.getXRot()); // resend remaining player data... (see ServerPlayer#changeDimension) - h.connection.send(new ClientboundChangeDifficultyPacket(h.serverLevel().getLevelData().getDifficulty(), h.serverLevel().getLevelData().isDifficultyLocked())); + h.connection.send(new ClientboundChangeDifficultyPacket(h.level().getLevelData().getDifficulty(), h.level().getLevelData().isDifficultyLocked())); h.connection.send(new ClientboundPlayerAbilitiesPacket(h.getAbilities())); - final PlayerList playerList = h.getServer().getPlayerList(); + final PlayerList playerList = ((ServerPlayerAccessor) h).accessor$server().getPlayerList(); playerList.sendAllPlayerInfo(h); playerList.sendPlayerPermissionLevel(h); - for(MobEffectInstance $$6 : h.getActiveEffects()) { + for (MobEffectInstance $$6 : h.getActiveEffects()) { h.connection.send(new ClientboundUpdateMobEffectPacket(h.getId(), $$6, false)); } h.connection.send(new ClientboundSetExperiencePacket(h.experienceProgress, h.totalExperience, h.experienceLevel)); diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/SheepData.java b/src/main/java/org/spongepowered/common/data/provider/entity/SheepData.java index ab56f4e9433..3194266a790 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/SheepData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/SheepData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.Sheep; +import net.minecraft.world.entity.animal.sheep.Sheep; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.DyeColor; import org.spongepowered.common.data.provider.DataProviderRegistrator; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/ShulkerBulletData.java b/src/main/java/org/spongepowered/common/data/provider/entity/ShulkerBulletData.java index 48785ff85dd..63bdc810717 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/ShulkerBulletData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/ShulkerBulletData.java @@ -24,6 +24,8 @@ */ package org.spongepowered.common.data.provider.entity; +import net.minecraft.world.entity.EntityReference; +import net.minecraft.world.entity.projectile.ShulkerBullet; import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.util.Direction; @@ -44,8 +46,14 @@ public static void register(final DataProviderRegistrator registrator) { .get(h -> h.accessor$currentMoveDirection() == null ? Direction.NONE : Constants.DirectionFunctions.getFor(h.accessor$currentMoveDirection())) .set((h, v) -> h.accessor$currentMoveDirection(Constants.DirectionFunctions.getFor(v))) .create(Keys.TARGET_ENTITY) - .get(h -> (Entity) h.accessor$finalTarget()) - .set((h, v) -> h.accessor$finalTarget((net.minecraft.world.entity.Entity) v)); + .get(h -> { + final var target = h.accessor$finalTarget(); + if (target == null || ((ShulkerBullet) h).level() == null) { + return null; + } + return (Entity) target.getEntity(((ShulkerBullet) h).level(), net.minecraft.world.entity.Entity.class); + }) + .set((h, v) -> h.accessor$finalTarget(EntityReference.of((net.minecraft.world.entity.Entity) v))); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/SlimeData.java b/src/main/java/org/spongepowered/common/data/provider/entity/SlimeData.java index 0477f97cded..593101c1cdf 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/SlimeData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/SlimeData.java @@ -24,9 +24,8 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.monster.Slime; +import net.minecraft.world.entity.monster.cubemob.Slime; import org.spongepowered.api.data.Keys; -import org.spongepowered.common.accessor.world.entity.monster.SlimeAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class SlimeData { @@ -42,7 +41,7 @@ public static void register(final DataProviderRegistrator registrator) { .get(h -> (double) (0.255F * (float) h.getSize())) .create(Keys.SIZE) .get(h -> h.getSize() - 1) - .set((h, v) -> ((SlimeAccessor) h).invoker$setSize(v + 1, false)); + .set((h, v) -> h.setSize(v + 1, false)); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/SpellcastingIllagerData.java b/src/main/java/org/spongepowered/common/data/provider/entity/SpellcastingIllagerData.java index 6cad9021fe5..8f515a26359 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/SpellcastingIllagerData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/SpellcastingIllagerData.java @@ -24,10 +24,10 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.monster.SpellcasterIllager; +import net.minecraft.world.entity.monster.illager.SpellcasterIllager; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.SpellType; -import org.spongepowered.common.accessor.world.entity.monster.SpellcasterIllagerAccessor; +import org.spongepowered.common.accessor.world.entity.monster.illager.SpellcasterIllagerAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class SpellcastingIllagerData { diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/SpiderData.java b/src/main/java/org/spongepowered/common/data/provider/entity/SpiderData.java index c66df4b8cf4..871ae19ed64 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/SpiderData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/SpiderData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.monster.Spider; +import net.minecraft.world.entity.monster.spider.Spider; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/StriderData.java b/src/main/java/org/spongepowered/common/data/provider/entity/StriderData.java new file mode 100644 index 00000000000..d8c4a45656e --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/provider/entity/StriderData.java @@ -0,0 +1,54 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.provider.entity; + +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.monster.Strider; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.spongepowered.api.data.Keys; +import org.spongepowered.common.data.provider.DataProviderRegistrator; + +public final class StriderData { + + private StriderData() { + } + + // @formatter:off + public static void register(final DataProviderRegistrator registrator) { + registrator + .asMutable(Strider.class) + .create(Keys.IS_SADDLED) + .get(Strider::isSaddled) + .set((h, v) -> { + if (v) { + h.setItemSlot(EquipmentSlot.SADDLE, new ItemStack(Items.SADDLE)); + } else { + h.setItemSlot(EquipmentSlot.SADDLE, ItemStack.EMPTY); + } + }); + } + // @formatter:on +} diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/TNTData.java b/src/main/java/org/spongepowered/common/data/provider/entity/TNTData.java index cac00f141a5..157e4f912ee 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/TNTData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/TNTData.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; +import net.minecraft.world.entity.EntityReference; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.item.PrimedTnt; import org.spongepowered.api.block.BlockState; @@ -43,7 +44,7 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(PrimedTnt.class) .create(Keys.DETONATOR) .get(h -> (Living) h.getOwner()) - .set((h, v) -> ((PrimedTntAccessor) h).accessor$owner((LivingEntity) v)) + .set((h, v) -> ((PrimedTntAccessor) h).accessor$owner(EntityReference.of((LivingEntity) v))) .create(Keys.BLOCK_STATE) .get(h -> (BlockState) h.getBlockState()) .set((h, v) -> h.setBlockState((net.minecraft.world.level.block.state.BlockState) v)); diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge_Attack_Impl.java b/src/main/java/org/spongepowered/common/data/provider/entity/TadpoleData.java similarity index 54% rename from forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge_Attack_Impl.java rename to src/main/java/org/spongepowered/common/data/provider/entity/TadpoleData.java index d584588fc38..0d9f64e0075 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge_Attack_Impl.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/TadpoleData.java @@ -22,29 +22,31 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.mixin.core.world.entity; +package org.spongepowered.common.data.provider.entity; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.stats.Stat; -import net.minecraft.world.entity.LivingEntity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.util.DamageEventUtil; +import net.minecraft.world.entity.animal.frog.Tadpole; +import org.spongepowered.api.data.Keys; +import org.spongepowered.common.accessor.world.entity.animal.frog.TadpoleAccessor; +import org.spongepowered.common.data.provider.DataProviderRegistrator; +import org.spongepowered.common.util.SpongeTicks; -@Mixin(LivingEntity.class) -public abstract class LivingEntityMixin_Forge_Attack_Impl { +public class TadpoleData { - protected DamageEventUtil.DamageEventResult attackImpl$actuallyHurtResult; - protected float attackImpl$actuallyHurtFinalDamage; - - /** - * Prevents {@link ServerPlayer#awardStat} from running before event - */ - @Redirect(method = "getDamageAfterMagicAbsorb", - at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;awardStat(Lnet/minecraft/stats/Stat;I)V")) - public void attackImpl$onAwardStatDamageResist(final ServerPlayer instance, final Stat resourceLocation, final int i) { - // do nothing + public static void register(final DataProviderRegistrator registrator) { + // @formatter:off + registrator + .asMutable(TadpoleAccessor.class) + .create(Keys.BABY_TICKS) + .get(h -> h.accessor$getAge() < 0 ? new SpongeTicks(Tadpole.ticksToBeFrog-h.accessor$getAge()) : null) + .setAnd((h, v) -> { + final int ticks = SpongeTicks.toSaturatedIntOrInfinite(v); + if (v.isInfinite() || ticks < 0) { + return false; + } + h.accessor$setAge(-ticks); + return true; + }) + ; + // @formatter:on } - } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/TameableData.java b/src/main/java/org/spongepowered/common/data/provider/entity/TameableData.java index c7f3ff898d0..e245c0b2f83 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/TameableData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/TameableData.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; +import net.minecraft.world.entity.EntityReference; import net.minecraft.world.entity.TamableAnimal; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -44,9 +45,14 @@ public static void register(final DataProviderRegistrator registrator) { .get(TamableAnimal::isTame) .set((h, v) -> h.setTame(v, true)) .create(Keys.OWNER) - .get(TamableAnimal::getOwnerUUID) + .get(t -> { + if (t.getOwnerReference() == null) { + return null; + } + return t.getOwnerReference().getUUID(); + }) .set((h, v) -> { - h.setOwnerUUID(v); + h.setOwnerReference(EntityReference.of(v)); h.setTame(v != null, true); }); } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/ThrowableItemProjectileData.java b/src/main/java/org/spongepowered/common/data/provider/entity/ThrowableItemProjectileData.java index 605b675f489..6e140cd74b6 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/ThrowableItemProjectileData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/ThrowableItemProjectileData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.projectile.ThrowableItemProjectile; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrowableItemProjectile; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.item.util.ItemStackUtil; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/TraderLlamaData.java b/src/main/java/org/spongepowered/common/data/provider/entity/TraderLlamaData.java index 79bfc39773f..05b2b1c43b5 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/TraderLlamaData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/TraderLlamaData.java @@ -28,7 +28,7 @@ import org.spongepowered.api.data.Keys; import org.spongepowered.api.util.Ticks; import org.spongepowered.common.accessor.world.entity.MobAccessor; -import org.spongepowered.common.accessor.world.entity.animal.horse.TraderLlamaAccessor; +import org.spongepowered.common.accessor.world.entity.animal.equine.TraderLlamaAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.util.SpongeTicks; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/TropicalFishData.java b/src/main/java/org/spongepowered/common/data/provider/entity/TropicalFishData.java index ee71f85dfaa..5ec5bf46e69 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/TropicalFishData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/TropicalFishData.java @@ -24,11 +24,11 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.entity.animal.fish.TropicalFish; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.DyeColor; import org.spongepowered.api.data.type.TropicalFishShape; -import org.spongepowered.common.accessor.world.entity.animal.TropicalFishAccessor; +import org.spongepowered.common.accessor.world.entity.animal.fish.TropicalFishAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class TropicalFishData { @@ -42,24 +42,14 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(TropicalFish.class) // Fish variant is: size | pattern << 8 | bodyColor << 16 | patternColor << 24 .create(Keys.TROPICAL_FISH_SHAPE) - .get(h -> (TropicalFishShape) (Object) h.getVariant()) - .set((h, v) -> h.setVariant((TropicalFish.Pattern) (Object) v)) + .get(h -> (TropicalFishShape) (Object) h.getPattern()) + .set((h, v) -> ((TropicalFishAccessor) h).invoker$setPattern((TropicalFish.Pattern) (Object) v)) .create(Keys.DYE_COLOR) .get(h -> (DyeColor) (Object) h.getBaseColor()) - .set((h, v) -> { - final net.minecraft.world.item.DyeColor patternColor = h.getPatternColor(); - final TropicalFish.Pattern type = h.getVariant(); - final int variant = TropicalFishAccessor.invoker$packVariant(type, (net.minecraft.world.item.DyeColor) (Object) v, patternColor); - ((TropicalFishAccessor) h).invoker$setPackedVariant(variant); - }) + .set((h, v) -> ((TropicalFishAccessor) h).invoker$setBaseColor((net.minecraft.world.item.DyeColor) (Object) v)) .create(Keys.PATTERN_COLOR) .get(h -> (DyeColor) (Object) h.getPatternColor()) - .set((h, v) -> { - final net.minecraft.world.item.DyeColor baseColor = h.getBaseColor(); - final TropicalFish.Pattern type = h.getVariant(); - final int variant = TropicalFishAccessor.invoker$packVariant(type, baseColor, (net.minecraft.world.item.DyeColor) (Object) v); - ((TropicalFishAccessor) h).invoker$setPackedVariant(variant); - }); + .set((h, v) -> ((TropicalFishAccessor) h).invoker$setPatternColor((net.minecraft.world.item.DyeColor) (Object) v)); } // @formatter:on diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/TurtleData.java b/src/main/java/org/spongepowered/common/data/provider/entity/TurtleData.java index 8dd4c38c7d8..8a969650f69 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/TurtleData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/TurtleData.java @@ -24,9 +24,9 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.Turtle; +import net.minecraft.world.entity.animal.turtle.Turtle; import org.spongepowered.api.data.Keys; -import org.spongepowered.common.accessor.world.entity.animal.TurtleAccessor; +import org.spongepowered.common.accessor.world.entity.animal.turtle.TurtleAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.util.VecHelper; @@ -53,8 +53,7 @@ public static void register(final DataProviderRegistrator registrator) { .get(TurtleAccessor::invoker$isGoingHome) .set(TurtleAccessor::invoker$setGoingHome) .create(Keys.IS_TRAVELING) - .get(TurtleAccessor::invoker$isTravelling) - .set(TurtleAccessor::invoker$setTravelling) + .get(t -> t.invoker$getTravelPos() != null) .create(Keys.TARGET_POSITION) .get(h -> VecHelper.toVector3i(h.invoker$getTravelPos())) .set((h, v) -> h.invoker$setTravelPos(VecHelper.toBlockPos(v))); diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/VanishableData.java b/src/main/java/org/spongepowered/common/data/provider/entity/VanishableData.java index 4fb03550573..5a031072059 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/VanishableData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/VanishableData.java @@ -49,7 +49,7 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.VANISH_STATE) .get(VanishableBridge::bridge$vanishState) .setAnd((h, v) -> { - if (h instanceof Entity && ((Entity) h).level().isClientSide) { + if (h instanceof Entity && ((Entity) h).level().isClientSide()) { return false; } h.bridge$vanishState(v); diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/VillagerData.java b/src/main/java/org/spongepowered/common/data/provider/entity/VillagerData.java index ea18f27364a..481a1e8513c 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/VillagerData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/VillagerData.java @@ -24,16 +24,18 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.npc.Villager; -import net.minecraft.world.entity.npc.VillagerProfession; -import net.minecraft.world.entity.npc.WanderingTrader; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.npc.villager.Villager; +import net.minecraft.world.entity.npc.villager.VillagerProfession; +import net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader; import net.minecraft.world.item.trading.MerchantOffer; import net.minecraft.world.item.trading.MerchantOffers; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.ProfessionType; import org.spongepowered.api.data.type.VillagerType; import org.spongepowered.api.item.merchant.TradeOffer; -import org.spongepowered.common.accessor.world.entity.npc.AbstractVillagerAccessor; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.accessor.world.entity.npc.villager.AbstractVillagerAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.util.SpongeTicks; @@ -49,30 +51,38 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(Villager.class) .create(Keys.PROFESSION_LEVEL) - .get(h -> h.getVillagerData().getLevel()) - .set((h, v) -> h.setVillagerData(h.getVillagerData().setLevel(v))) + .get(h -> h.getVillagerData().level()) + .set((h, v) -> h.setVillagerData(h.getVillagerData().withLevel(v))) .create(Keys.PROFESSION_TYPE) - .get(h -> (ProfessionType) (Object) h.getVillagerData().getProfession()) - .set((h, v) -> h.setVillagerData(h.getVillagerData().setProfession((VillagerProfession) (Object) v))) + .get(h -> (ProfessionType) (Object) h.getVillagerData().profession().value()) + .set((h, v) -> { + final var villagerType = SpongeCommon.vanillaRegistry(Registries.VILLAGER_PROFESSION) + .wrapAsHolder((VillagerProfession) (Object) v); + h.setVillagerData(h.getVillagerData().withProfession(villagerType)); + }) .create(Keys.EXPERIENCE) .get(Villager::getVillagerXp) .set(Villager::setVillagerXp) .delete(h -> h.setVillagerXp(0)) .create(Keys.EXPERIENCE_LEVEL) - .get(h -> h.getVillagerData().getLevel()) + .get(h -> h.getVillagerData().level()) .setAnd((h, v) -> { if (v < 1) { return false; } - h.setVillagerData(h.getVillagerData().setLevel(v)); + h.setVillagerData(h.getVillagerData().withLevel(v)); return true; }) .create(Keys.TRADE_OFFERS) .get(h -> h.getOffers().stream().map(TradeOffer.class::cast).collect(Collectors.toList())) .set((h, v) -> h.setOffers(v.stream().map(MerchantOffer.class::cast).collect(Collectors.toCollection(MerchantOffers::new)))) .create(Keys.VILLAGER_TYPE) - .get(h -> (VillagerType) (Object) h.getVillagerData().getType()) - .set((h, v) -> h.setVillagerData(h.getVillagerData().setType((net.minecraft.world.entity.npc.VillagerType) (Object) v))) + .get(h -> (VillagerType) (Object) h.getVillagerData().type().value()) + .set((h, v) -> { + final var villagerType = SpongeCommon.vanillaRegistry(Registries.VILLAGER_TYPE) + .wrapAsHolder((net.minecraft.world.entity.npc.villager.VillagerType) (Object) v); + h.setVillagerData(h.getVillagerData().withType(villagerType)); + }) .create(Keys.IS_UNHAPPY) .get(h -> h.getUnhappyCounter() > 0) .create(Keys.UNHAPPY_TIME) diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/VindicatorData.java b/src/main/java/org/spongepowered/common/data/provider/entity/VindicatorData.java index 4c442dfaa6b..7ad33e0f335 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/VindicatorData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/VindicatorData.java @@ -25,7 +25,7 @@ package org.spongepowered.common.data.provider.entity; import org.spongepowered.api.data.Keys; -import org.spongepowered.common.accessor.world.entity.monster.VindicatorAccessor; +import org.spongepowered.common.accessor.world.entity.monster.illager.VindicatorAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class VindicatorData { diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/WitherData.java b/src/main/java/org/spongepowered/common/data/provider/entity/WitherData.java index c61d2c863f0..c4eb1e8e689 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/WitherData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/WitherData.java @@ -45,7 +45,7 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(WitherBoss.class) .create(Keys.WITHER_TARGETS) .get(h -> Stream.of(h.getAlternativeTarget(0), h.getAlternativeTarget(1), h.getAlternativeTarget(2)) - .map(id -> h.getCommandSenderWorld().getEntity(id)) + .map(id -> h.level().getEntity(id)) // TODO filter null? .filter(Objects::nonNull) .map(org.spongepowered.api.entity.Entity.class::cast) .collect(Collectors.toList()) diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/WolfData.java b/src/main/java/org/spongepowered/common/data/provider/entity/WolfData.java index 3328216b1f4..f355016aed5 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/WolfData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/WolfData.java @@ -24,10 +24,11 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.animal.Wolf; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.animal.wolf.Wolf; import org.spongepowered.api.data.Keys; -import org.spongepowered.api.data.type.DyeColor; -import org.spongepowered.common.accessor.world.entity.animal.WolfAccessor; +import org.spongepowered.common.accessor.world.entity.animal.wolf.WolfAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class WolfData { @@ -39,9 +40,6 @@ private WolfData() { public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(Wolf.class) - .create(Keys.DYE_COLOR) - .get(h -> (DyeColor) (Object) h.getCollarColor()) - .set((h, v) -> ((WolfAccessor)h).invoker$setCollarColor((net.minecraft.world.item.DyeColor) (Object) v)) .create(Keys.IS_BEGGING_FOR_FOOD) .get(Wolf::isInterested) .set(Wolf::setIsInterested) @@ -54,6 +52,17 @@ public static void register(final DataProviderRegistrator registrator) { accessor.accessor$shakeAnim(0f); accessor.accessor$shakeAnimO(0f); }); + + // @formatter:on + final var wolf = registrator.asMutable(Wolf.class); + final var providers = EntityDataProviders.of( + EntityDataProviders.holderOf(Keys.WOLF_VARIANT, DataComponents.WOLF_VARIANT, Registries.WOLF_VARIANT), + EntityDataProviders.holderOf(Keys.WOLF_SOUND_VARIANT, DataComponents.WOLF_SOUND_VARIANT, Registries.WOLF_SOUND_VARIANT), + EntityDataProviders.enumOf(Keys.DYE_COLOR, DataComponents.WOLF_COLLAR) + ); + for (var provider : providers) { + provider.applyToRegistrator(wolf); + } } - // @formatter:on + } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/ZombieData.java b/src/main/java/org/spongepowered/common/data/provider/entity/ZombieData.java index 713841f069c..1b4d42c074e 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/ZombieData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/ZombieData.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.monster.Zombie; +import net.minecraft.world.entity.monster.zombie.Zombie; import org.spongepowered.api.data.Keys; import org.spongepowered.common.data.provider.DataProviderRegistrator; diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/ZombieVillagerData.java b/src/main/java/org/spongepowered/common/data/provider/entity/ZombieVillagerData.java index 4810aad85aa..3454f41c763 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/ZombieVillagerData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/ZombieVillagerData.java @@ -24,11 +24,13 @@ */ package org.spongepowered.common.data.provider.entity; -import net.minecraft.world.entity.monster.ZombieVillager; -import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.monster.zombie.ZombieVillager; +import net.minecraft.world.entity.npc.villager.VillagerProfession; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.ProfessionType; import org.spongepowered.api.data.type.VillagerType; +import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class ZombieVillagerData { @@ -41,14 +43,20 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(ZombieVillager.class) .create(Keys.PROFESSION_LEVEL) - .get(h -> h.getVillagerData().getLevel()) - .set((h, v) -> h.setVillagerData(h.getVillagerData().setLevel(v))) + .get(h -> h.getVillagerData().level()) + .set((h, v) -> h.setVillagerData(h.getVillagerData().withLevel(v))) .create(Keys.PROFESSION_TYPE) - .get(h -> (ProfessionType) (Object) h.getVillagerData().getProfession()) - .set((h, v) -> h.setVillagerData(h.getVillagerData().setProfession((VillagerProfession) (Object) v))) + .get(h -> (ProfessionType) (Object) h.getVillagerData().profession().value()) + .set((h, v) -> { + final var villagerProfession = SpongeCommon.vanillaRegistry(Registries.VILLAGER_PROFESSION).wrapAsHolder((VillagerProfession) (Object) v); + h.setVillagerData(h.getVillagerData().withProfession(villagerProfession)); + }) .create(Keys.VILLAGER_TYPE) - .get(h -> (VillagerType) (Object) h.getVillagerData().getType()) - .set((h, v) -> h.setVillagerData(h.getVillagerData().setType((net.minecraft.world.entity.npc.VillagerType) (Object) v))); + .get(h -> (VillagerType) (Object) h.getVillagerData().type().value()) + .set((h, v) -> { + final var villagerType = SpongeCommon.vanillaRegistry(Registries.VILLAGER_TYPE).wrapAsHolder((net.minecraft.world.entity.npc.villager.VillagerType) (Object) v); + h.setVillagerData(h.getVillagerData().withType(villagerType)); + }); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/entity/ZombifiedPiglinData.java b/src/main/java/org/spongepowered/common/data/provider/entity/ZombifiedPiglinData.java index d0867fcdd3c..292afe3a555 100644 --- a/src/main/java/org/spongepowered/common/data/provider/entity/ZombifiedPiglinData.java +++ b/src/main/java/org/spongepowered/common/data/provider/entity/ZombifiedPiglinData.java @@ -25,7 +25,7 @@ package org.spongepowered.common.data.provider.entity; import org.spongepowered.api.data.Keys; -import org.spongepowered.common.accessor.world.entity.monster.ZombifiedPiglinAccessor; +import org.spongepowered.common.accessor.world.entity.monster.zombie.ZombifiedPiglinAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; import java.util.SplittableRandom; @@ -42,7 +42,7 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.ANGER_LEVEL) .get(ZombifiedPiglinAccessor::accessor$remainingPersistentAngerTime) .set(ZombifiedPiglinAccessor::accessor$remainingPersistentAngerTime) - .resetOnDelete(400 + new SplittableRandom().nextInt(400)); + .resetOnDelete(() -> (long)(400 + new SplittableRandom().nextInt(400))); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/ArmorItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/ArmorItemStackData.java index e68c904445f..9c6cacd5697 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/ArmorItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/ArmorItemStackData.java @@ -24,16 +24,33 @@ */ package org.spongepowered.common.data.provider.item.stack; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; import net.minecraft.core.component.DataComponents; -import net.minecraft.world.item.ArmorItem; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.ItemAttributeModifiers; +import net.minecraft.world.item.equipment.EquipmentAsset; +import net.minecraft.world.item.equipment.EquipmentAssets; +import net.minecraft.world.item.equipment.Equippable; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.data.Keys; -import org.spongepowered.api.data.type.ArmorMaterial; -import org.spongepowered.api.item.inventory.equipment.EquipmentTypes; +import org.spongepowered.api.effect.sound.SoundType; +import org.spongepowered.api.entity.EntityType; +import org.spongepowered.api.item.inventory.equipment.EquipmentType; import org.spongepowered.api.item.recipe.smithing.ArmorTrim; +import org.spongepowered.api.registry.RegistryEntry; +import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.common.data.provider.DataProviderRegistrator; +import java.util.Optional; +import java.util.stream.Collectors; + public final class ArmorItemStackData { private ArmorItemStackData() { @@ -43,17 +60,95 @@ private ArmorItemStackData() { public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(ItemStack.class) + .create(Keys.ALLOWED_ENTITIES) + .get(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null || equippable.allowedEntities().isEmpty()) { + return null; + } + return equippable.allowedEntities().get().stream() + .map(Holder::value) + .map(e -> (EntityType) e) + .collect(Collectors.toSet()); + }) + .setAnd((h, v) -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + final HolderSet> holderSet = HolderSet.direct( + e -> BuiltInRegistries.ENTITY_TYPE.wrapAsHolder((net.minecraft.world.entity.EntityType) e), + v + ); + h.set(DataComponents.EQUIPPABLE, ArmorItemStackData.asBuilder(equippable).setAllowedEntities(holderSet).build()); + return true; + }) + .deleteAnd(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + h.set(DataComponents.EQUIPPABLE, new Equippable( + equippable.slot(), + equippable.equipSound(), + equippable.assetId(), + equippable.cameraOverlay(), + Optional.empty(), + equippable.dispensable(), + equippable.swappable(), + equippable.damageOnHurt(), + equippable.equipOnInteract(), + equippable.canBeSheared(), + equippable.shearingSound() + )); + return true; + }) .create(Keys.ARMOR_MATERIAL) .get(h -> { - if (h.getItem() instanceof ArmorItem armorItem) { - return (ArmorMaterial) (Object) armorItem.getMaterial().value(); + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return null; } - return null; + return equippable.assetId() + .map(rl -> (ResourceKey) (Object) rl.identifier()) + .flatMap(rk -> RegistryTypes.ARMOR_MATERIAL.get().findEntry(rk)) + .map(RegistryEntry::value) + .orElse(null); + }) + .setAnd((h, v) -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + final ResourceKey key = RegistryTypes.ARMOR_MATERIAL.get().valueKey(v); + final net.minecraft.resources.ResourceKey assetKey = + net.minecraft.resources.ResourceKey.create(EquipmentAssets.ROOT_ID, (Identifier) (Object) key); + h.set(DataComponents.EQUIPPABLE, ArmorItemStackData.asBuilder(equippable).setAsset(assetKey).build()); + return true; + }) + .deleteAnd(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + h.set(DataComponents.EQUIPPABLE, new Equippable( + equippable.slot(), + equippable.equipSound(), + Optional.empty(), + equippable.cameraOverlay(), + equippable.allowedEntities(), + equippable.dispensable(), + equippable.swappable(), + equippable.damageOnHurt(), + equippable.equipOnInteract(), + equippable.canBeSheared(), + equippable.shearingSound() + )); + return true; }) - .supports(h -> h.getItem() instanceof ArmorItem) .create(Keys.ARMOR_TRIM) .get(h -> { - final net.minecraft.world.item.armortrim.@Nullable ArmorTrim trim = h.get(DataComponents.TRIM); + final net.minecraft.world.item.equipment.trim.@Nullable ArmorTrim trim = h.get(DataComponents.TRIM); if (trim != null) { return (ArmorTrim) (Object) trim; } @@ -64,37 +159,215 @@ public static void register(final DataProviderRegistrator registrator) { h.remove(DataComponents.TRIM); return; } - h.set(DataComponents.TRIM, (net.minecraft.world.item.armortrim.ArmorTrim) v); + h.set(DataComponents.TRIM, (net.minecraft.world.item.equipment.trim.ArmorTrim) (Object) v); }) .delete(h -> h.remove(DataComponents.TRIM)) - .supports(h -> h.getItem() instanceof ArmorItem) + .create(Keys.CAMERA_OVERLAY) + .get(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null || equippable.cameraOverlay().isEmpty()) { + return null; + } + return (ResourceKey) (Object) equippable.cameraOverlay().get(); + }) + .setAnd((h, v) -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + h.set(DataComponents.EQUIPPABLE, ArmorItemStackData.asBuilder(equippable).setCameraOverlay((Identifier) (Object) v).build()); + return true; + }) + .deleteAnd(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + h.set(DataComponents.EQUIPPABLE, new Equippable( + equippable.slot(), + equippable.equipSound(), + equippable.assetId(), + Optional.empty(), + equippable.allowedEntities(), + equippable.dispensable(), + equippable.swappable(), + equippable.damageOnHurt(), + equippable.equipOnInteract(), + equippable.canBeSheared(), + equippable.shearingSound() + )); + return true; + }) + .create(Keys.CAN_BE_SHEARED) + .get(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return null; + } + return equippable.canBeSheared(); + }) + .setAnd((h, v) -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + h.set(DataComponents.EQUIPPABLE, ArmorItemStackData.asBuilder(equippable).setCanBeSheared(v).build()); + return true; + }) .create(Keys.DAMAGE_ABSORPTION) .get(h -> { - if (h.getItem() instanceof ArmorItem armorItem) { - return (double) armorItem.getDefense(); + final @Nullable ItemAttributeModifiers modifiersContainer = h.get(DataComponents.ATTRIBUTE_MODIFIERS); + if (modifiersContainer == null) { + return null; } - return null; + return modifiersContainer.modifiers().stream() + .filter(e1 -> e1.attribute() == Attributes.ARMOR) + .findFirst() + .map(e -> e.modifier().amount()) + .orElse(null); + }) + .create(Keys.DAMAGE_ON_HURT) + .get(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return null; + } + return equippable.damageOnHurt(); + }) + .setAnd((h, v) -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + h.set(DataComponents.EQUIPPABLE, ArmorItemStackData.asBuilder(equippable).setDamageOnHurt(v).build()); + return true; + }) + .create(Keys.EQUIP_ON_INTERACT) + .get(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return null; + } + return equippable.equipOnInteract(); + }) + .setAnd((h, v) -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + h.set(DataComponents.EQUIPPABLE, ArmorItemStackData.asBuilder(equippable).setEquipOnInteract(v).build()); + return true; + }) + .create(Keys.EQUIP_SOUND) + .get(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return null; + } + return (SoundType) (Object) equippable.equipSound().value(); + }) + .setAnd((h, v) -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + h.set(DataComponents.EQUIPPABLE, ArmorItemStackData.asBuilder(equippable).setEquipSound(Holder.direct((SoundEvent) (Object) v)).build()); + return true; }) - .supports(h -> h.getItem() instanceof ArmorItem) .create(Keys.EQUIPMENT_TYPE) .get(h -> { - if (h.getItem() instanceof ArmorItem armorItem) { - switch (armorItem.getEquipmentSlot()) { - case FEET: - return EquipmentTypes.FEET.get(); - case LEGS: - return EquipmentTypes.LEGS.get(); - case CHEST: - return EquipmentTypes.CHEST.get(); - case HEAD: - return EquipmentTypes.HEAD.get(); - default: - break; - } + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return null; } - return null; + return (EquipmentType) (Object) equippable.slot(); + }) + .set((h, v) -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + final EquipmentSlot slot = (EquipmentSlot) (Object) v; + if (equippable == null) { + h.set(DataComponents.EQUIPPABLE, Equippable.builder(slot).build()); + return; + } + h.set(DataComponents.EQUIPPABLE, new Equippable( + slot, + equippable.equipSound(), + equippable.assetId(), + equippable.cameraOverlay(), + equippable.allowedEntities(), + equippable.dispensable(), + equippable.swappable(), + equippable.damageOnHurt(), + equippable.equipOnInteract(), + equippable.canBeSheared(), + equippable.shearingSound() + )); }) - .supports(h -> h.getItem() instanceof ArmorItem); + .delete(h -> h.remove(DataComponents.EQUIPPABLE)) + .create(Keys.IS_DISPENSABLE) + .get(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return null; + } + return equippable.dispensable(); + }) + .setAnd((h, v) -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + h.set(DataComponents.EQUIPPABLE, ArmorItemStackData.asBuilder(equippable).setDispensable(v).build()); + return true; + }) + .create(Keys.IS_SWAPPABLE) + .get(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return null; + } + return equippable.swappable(); + }) + .setAnd((h, v) -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + h.set(DataComponents.EQUIPPABLE, ArmorItemStackData.asBuilder(equippable).setSwappable(v).build()); + return true; + }) + .create(Keys.SHEARING_SOUND) + .get(h -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return null; + } + return (SoundType) (Object) equippable.shearingSound().value(); + }) + .setAnd((h, v) -> { + final @Nullable Equippable equippable = h.get(DataComponents.EQUIPPABLE); + if (equippable == null) { + return false; + } + h.set(DataComponents.EQUIPPABLE, ArmorItemStackData.asBuilder(equippable).setShearingSound(Holder.direct((SoundEvent) (Object) v)).build()); + return true; + }); } // @formatter:on + + private static Equippable.Builder asBuilder(final Equippable equippable) { + final Equippable.Builder builder = Equippable.builder(equippable.slot()) + .setEquipSound(equippable.equipSound()) + .setDispensable(equippable.dispensable()) + .setSwappable(equippable.swappable()) + .setDamageOnHurt(equippable.damageOnHurt()) + .setEquipOnInteract(equippable.equipOnInteract()) + .setCanBeSheared(equippable.canBeSheared()) + .setShearingSound(equippable.shearingSound()); + equippable.assetId().ifPresent(builder::setAsset); + equippable.cameraOverlay().ifPresent(builder::setCameraOverlay); + equippable.allowedEntities().ifPresent(builder::setAllowedEntities); + return builder; + } + } diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/BlockItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/BlockItemStackData.java index bf4a788432b..1c281273d53 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/BlockItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/BlockItemStackData.java @@ -25,7 +25,6 @@ package org.spongepowered.common.data.provider.item.stack; import net.minecraft.core.component.DataComponents; -import net.minecraft.world.LockCode; import net.minecraft.world.item.BannerItem; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; @@ -52,18 +51,18 @@ public static void register(final DataProviderRegistrator registrator) { return ((DyeColorBlockBridge) block).bridge$getDyeColor().orElse(null); }) .supports(h -> h.getItem() instanceof BlockItem && !(h.getItem() instanceof BannerItem)) - .create(Keys.LOCK_TOKEN) - .get(h -> { - var component = h.getOrDefault(DataComponents.LOCK, LockCode.NO_LOCK); - return component.key().isEmpty() ? null : component.key(); - }) - .set((h, v) -> { - if (v.isEmpty()) { - h.remove(DataComponents.LOCK); - return; - } - h.set(DataComponents.LOCK, new LockCode(v)); - }) + .create(Keys.LOCK_TOKEN) // TODO - Implement Item Predicates +// .get(h -> { +// var component = h.getOrDefault(DataComponents.LOCK, LockCode.NO_LOCK); +// return component.key().isEmpty() ? null : component.key(); +// }) +// .set((h, v) -> { +// if (v.isEmpty()) { +// h.remove(DataComponents.LOCK); +// return; +// } +// h.set(DataComponents.LOCK, new LockCode(v)); +// }) .delete(h -> h.remove(DataComponents.LOCK)) .supports(h -> { if (!(h.getItem() instanceof BlockItem)) { diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/BlockTypeItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/BlockTypeItemStackData.java index f05aa2200fb..70d663a2569 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/BlockTypeItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/BlockTypeItemStackData.java @@ -24,14 +24,16 @@ */ package org.spongepowered.common.data.provider.item.stack; -import net.minecraft.advancements.critereon.BlockPredicate; +import net.minecraft.advancements.predicates.BlockPredicate; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.item.AdventureModePredicate; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.block.BlockType; import org.spongepowered.api.data.Keys; import org.spongepowered.common.accessor.world.item.AdventureModePredicateAccessor; @@ -52,14 +54,14 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(ItemStack.class) .create(Keys.BREAKABLE_BLOCK_TYPES) .get(h -> BlockTypeItemStackData.get(h, DataComponents.CAN_BREAK)) - .set((h, v) -> BlockTypeItemStackData.set(h, DataComponents.CAN_BREAK, v)) + .setAnd((h, v) -> BlockTypeItemStackData.set(h, DataComponents.CAN_BREAK, v)) .create(Keys.PLACEABLE_BLOCK_TYPES) .get(h -> BlockTypeItemStackData.get(h, DataComponents.CAN_PLACE_ON)) - .set((h, v) -> BlockTypeItemStackData.set(h, DataComponents.CAN_PLACE_ON, v)); + .setAnd((h, v) -> BlockTypeItemStackData.set(h, DataComponents.CAN_PLACE_ON, v)); } // @formatter:on - private static Set get(final ItemStack stack, final DataComponentType component) { + private static @Nullable Set get(final ItemStack stack, final DataComponentType component) { // TODO change API type to predicates final AdventureModePredicate predicate = stack.get(component); if (predicate != null) { @@ -78,8 +80,9 @@ private static boolean set(final ItemStack stack, final DataComponentType value if (value.isEmpty()) { return BookPagesItemStackData.delete(holder, component); } - final var registry = SpongeCommon.server().registryAccess().registryOrThrow(Registries.ENCHANTMENT); + final var registry = SpongeCommon.vanillaRegistry(Registries.ENCHANTMENT); holder.update(component, ItemEnchantments.EMPTY, ench -> { final ItemEnchantments.Mutable mutable = new ItemEnchantments.Mutable(ench); mutable.keySet().clear(); diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/DyeableItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/DyeableItemStackData.java index 0308c122358..bc0e486d706 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/DyeableItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/DyeableItemStackData.java @@ -25,7 +25,6 @@ package org.spongepowered.common.data.provider.item.stack; import net.minecraft.core.component.DataComponents; -import net.minecraft.tags.ItemTags; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.DyedItemColor; import org.spongepowered.api.data.Keys; @@ -43,9 +42,8 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(ItemStack.class) .create(Keys.COLOR) .get(h -> h.has(DataComponents.DYED_COLOR) ? Color.ofRgb(h.get(DataComponents.DYED_COLOR).rgb()) : null) - .set((h, v) -> h.update(DataComponents.DYED_COLOR, new DyedItemColor(0, true), prev -> new DyedItemColor(v.rgb(), prev.showInTooltip()))) - .delete(h -> h.set(DataComponents.DYED_COLOR, null)) - .supports(h -> h.is(ItemTags.DYEABLE)); + .set((h, v) -> h.set(DataComponents.DYED_COLOR, new DyedItemColor(v.rgb()))) + .delete(h -> h.remove(DataComponents.DYED_COLOR)); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/HideFlagsItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/HideFlagsItemStackData.java index 5938e51050d..44c1a702bee 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/HideFlagsItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/HideFlagsItemStackData.java @@ -24,23 +24,16 @@ */ package org.spongepowered.common.data.provider.item.stack; -import net.minecraft.advancements.critereon.BlockPredicate; +import com.google.common.collect.ImmutableMap; +import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; -import net.minecraft.util.Unit; -import net.minecraft.world.item.AdventureModePredicate; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.component.ItemAttributeModifiers; -import net.minecraft.world.item.component.Unbreakable; -import net.minecraft.world.item.enchantment.ItemEnchantments; -import org.jetbrains.annotations.NotNull; +import net.minecraft.world.item.component.TooltipDisplay; +import org.spongepowered.api.data.Key; import org.spongepowered.api.data.Keys; -import org.spongepowered.common.accessor.world.item.AdventureModePredicateAccessor; -import org.spongepowered.common.accessor.world.item.enchantment.ItemEnchantmentsAccessor; +import org.spongepowered.api.data.value.Value; import org.spongepowered.common.data.provider.DataProviderRegistrator; -import java.util.Collections; -import java.util.List; - public final class HideFlagsItemStackData { private HideFlagsItemStackData() { @@ -48,62 +41,51 @@ private HideFlagsItemStackData() { // @formatter:off public static void register(final DataProviderRegistrator registrator) { - registrator - .asMutable(ItemStack.class) - .create(Keys.HIDE_ATTRIBUTES) - .get(h -> h.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY).showInTooltip()) - .set((h, v) -> h.update(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY, p -> new ItemAttributeModifiers(p.modifiers(), !v))) - .create(Keys.HIDE_CAN_DESTROY) - .get(h -> h.has(DataComponents.CAN_BREAK) && !h.get(DataComponents.CAN_BREAK).showInTooltip()) - .set((h, v) -> h.set(DataComponents.CAN_BREAK, HideFlagsItemStackData.newAdventureModePredicate(h, !v))) - .create(Keys.HIDE_CAN_PLACE) - .get(h -> { - final var predicate = h.get(DataComponents.CAN_BREAK); - if (predicate == null) { - return false; - } - return !predicate.showInTooltip(); - }) - .set((h, v) -> h.set(DataComponents.CAN_PLACE_ON, HideFlagsItemStackData.newAdventureModePredicate(h, !v))) - .create(Keys.HIDE_ENCHANTMENTS) - .get(h -> ((ItemEnchantmentsAccessor)h.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY)).accessor$showInTooltip()) - .set((h, v) -> h.set(DataComponents.ENCHANTMENTS, HideFlagsItemStackData.newItemEnchantments(h, !v))) - .create(Keys.HIDE_STORED_ENCHANTMENTS) - .get(h -> ((ItemEnchantmentsAccessor)h.getOrDefault(DataComponents.STORED_ENCHANTMENTS, ItemEnchantments.EMPTY)).accessor$showInTooltip()) - .set((h, v) -> h.set(DataComponents.STORED_ENCHANTMENTS, HideFlagsItemStackData.newItemEnchantments(h, !v))) - .create(Keys.HIDE_MISCELLANEOUS) - .get(h -> h.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != Unit.INSTANCE) - .set((h, v) -> h.set(DataComponents.HIDE_ADDITIONAL_TOOLTIP, v ? Unit.INSTANCE : null)) - .create(Keys.HIDE_UNBREAKABLE) - .get(h -> h.has(DataComponents.UNBREAKABLE) && !h.get(DataComponents.UNBREAKABLE).showInTooltip()) - .set((h, v) -> { - if (h.has(DataComponents.UNBREAKABLE)) { - h.set(DataComponents.UNBREAKABLE, new Unbreakable(v)); - } // else TODO not supported? - }) + final ImmutableMap>, DataComponentType> keyToComponentMapping = new ImmutableMap.Builder>, DataComponentType>() + .put(Keys.HIDE_ARMOR_TRIM, DataComponents.TRIM) + .put(Keys.HIDE_ATTRIBUTES, DataComponents.ATTRIBUTE_MODIFIERS) + .put(Keys.HIDE_BANNER_PATTERNS, DataComponents.BANNER_PATTERNS) + .put(Keys.HIDE_BUNDLE_CONTENTS, DataComponents.BUNDLE_CONTENTS) + .put(Keys.HIDE_CAN_DESTROY, DataComponents.CAN_BREAK) + .put(Keys.HIDE_CAN_PLACE, DataComponents.CAN_PLACE_ON) + .put(Keys.HIDE_COLOR, DataComponents.DYED_COLOR) + .put(Keys.HIDE_ENCHANTMENTS, DataComponents.ENCHANTMENTS) + .put(Keys.HIDE_INSTRUMENT, DataComponents.INSTRUMENT) + .put(Keys.HIDE_MISCELLANEOUS, DataComponents.CUSTOM_DATA) + .put(Keys.HIDE_MUSIC_DISC, DataComponents.JUKEBOX_PLAYABLE) + .put(Keys.HIDE_POTION_EFFECTS, DataComponents.POTION_CONTENTS) + .put(Keys.HIDE_STORED_ENCHANTMENTS, DataComponents.STORED_ENCHANTMENTS) + .put(Keys.HIDE_UNBREAKABLE, DataComponents.UNBREAKABLE) + .build(); + + final var mutableItem = registrator + .asMutable(ItemStack.class); + for (final var entry : keyToComponentMapping.entrySet()) { + mutableItem + .create(entry.getKey()) + .get(h -> h.has(DataComponents.TOOLTIP_DISPLAY) && !h.getOrDefault(DataComponents.TOOLTIP_DISPLAY, TooltipDisplay.DEFAULT).shows(entry.getValue())) + .set((h, v) -> h.update(DataComponents.TOOLTIP_DISPLAY, TooltipDisplay.DEFAULT, p -> p.withHidden(entry.getValue(), v))); + } + mutableItem .create(Keys.HIDE_TOOLTIP) - .get(h -> h.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != Unit.INSTANCE) - .set((h, v) -> h.set(DataComponents.HIDE_ADDITIONAL_TOOLTIP, v ? Unit.INSTANCE : null)) + .get(h -> h.getOrDefault(DataComponents.TOOLTIP_DISPLAY, TooltipDisplay.DEFAULT).hideTooltip()) + .set((h, v) -> h.update(DataComponents.TOOLTIP_DISPLAY, TooltipDisplay.DEFAULT, p -> new TooltipDisplay(v, p.hiddenComponents()))) ; // TODO missing show_in_tooltip (DYED_COLOR, more?) } // @formatter:on - - @NotNull - private static AdventureModePredicate newAdventureModePredicate(final ItemStack h, final boolean showInTooltip) { - if (h.has(DataComponents.CAN_BREAK)) { - final List $$0 = ((AdventureModePredicateAccessor) h.get(DataComponents.CAN_BREAK)).accessor$predicates(); - return new AdventureModePredicate($$0, showInTooltip); - } - return new AdventureModePredicate(Collections.emptyList(), showInTooltip); - } - - @NotNull - private static ItemEnchantments newItemEnchantments(final ItemStack h, final boolean showInTooltip) { - final ItemEnchantmentsAccessor enchantments = (ItemEnchantmentsAccessor) h.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY);; - return ItemEnchantmentsAccessor.invoker$new(enchantments.accessor$enchantments(), showInTooltip); - } +// +// @NotNull +// private static AdventureModePredicate newAdventureModePredicate(final ItemStack h, final DataComponentType old, final boolean showInTooltip) { +// return new AdventureModePredicate(((AdventureModePredicateAccessor) h.get(old)).accessor$predicates(), showInTooltip); +// } +// +// @NotNull +// private static ItemEnchantments newItemEnchantments(final ItemStack h, final boolean showInTooltip) { +// final ItemEnchantmentsAccessor enchantments = (ItemEnchantmentsAccessor) h.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY);; +// return ItemEnchantmentsAccessor.invoker$new(enchantments.accessor$enchantments(), showInTooltip); +// } } diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/ItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/ItemStackData.java index a6db908d124..e30b5475b9c 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/ItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/ItemStackData.java @@ -25,44 +25,55 @@ package org.spongepowered.common.data.provider.item.stack; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.tags.DamageTypeTags; import net.minecraft.util.StringUtil; import net.minecraft.util.Unit; -import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.damagesource.DamageTypes; import net.minecraft.world.food.FoodProperties; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemUseAnimation; import net.minecraft.world.item.Rarity; import net.minecraft.world.item.component.BlockItemStateProperties; import net.minecraft.world.item.component.ChargedProjectiles; +import net.minecraft.world.item.component.Consumable; import net.minecraft.world.item.component.CustomModelData; +import net.minecraft.world.item.component.DamageResistant; +import net.minecraft.world.item.component.DeathProtection; +import net.minecraft.world.item.component.ItemAttributeModifiers; import net.minecraft.world.item.component.ItemContainerContents; import net.minecraft.world.item.component.ItemLore; -import net.minecraft.world.item.component.Unbreakable; -import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; +import net.minecraft.world.item.component.UseCooldown; +import net.minecraft.world.item.component.UseRemainder; +import net.minecraft.world.item.component.Weapon; +import net.minecraft.world.item.consume_effects.ConsumeEffect; import net.minecraft.world.level.block.state.properties.Property; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.Platform; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.data.DataTransactionResult; import org.spongepowered.api.data.Keys; -import org.spongepowered.api.effect.potion.PotionEffect; +import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.entity.attribute.ItemAttribute; import org.spongepowered.api.item.ItemRarity; import org.spongepowered.api.item.ItemType; import org.spongepowered.api.item.inventory.Inventory; import org.spongepowered.api.item.inventory.Slot; +import org.spongepowered.api.util.Color; import org.spongepowered.api.util.Ticks; -import org.spongepowered.api.util.weighted.ChanceTable; -import org.spongepowered.api.util.weighted.NestedTableEntry; -import org.spongepowered.api.util.weighted.TableEntry; -import org.spongepowered.api.util.weighted.WeightedObject; -import org.spongepowered.api.util.weighted.WeightedTable; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.item.util.ItemStackUtil; +import org.spongepowered.common.util.Constants; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -72,7 +83,8 @@ @SuppressWarnings({"unchecked", "UnstableApiUsage"}) public final class ItemStackData { - public static final FoodProperties DEFAULT_FOOD_PROPERTIES = new FoodProperties(0, 0, false, 1.6F, Optional.empty(), List.of()); + public static final FoodProperties DEFAULT_FOOD_PROPERTIES = new FoodProperties(0, 0, false); + public static final Consumable DEFAULT_CONSUMABLE_PROPERTIES = new Consumable(1.6F, ItemUseAnimation.EAT, SoundEvents.GENERIC_EAT, true, List.of()); private ItemStackData() { } @@ -80,7 +92,6 @@ private ItemStackData() { // @formatter:off public static void register(final DataProviderRegistrator registrator) { // TODO DataComponents.SUSPICIOUS_STEW_EFFECTS - // TODO maybe DataComponents.ATTRIBUTE_MODIFIERS as keys? // TODO DataComponents.BUNDLE_CONTENTS also check for Shulker Boxes? - removing the component prevents using the bundle // TODO DataComponents.CONTAINER_LOOT for containers with loottable data, also for blockentity? // TODO DataComponents.BLOCK_ENTITY_DATA maybe expose as raw DataContainer? (id MUST have block entity type) @@ -93,57 +104,53 @@ public static void register(final DataProviderRegistrator registrator) { // TODO DataComponents.INSTRUMENT goat horn + API type + duration + range // TODO DataComponents.RECIPES - for Items.KNOWLEDGE_BOOK // TODO DataComponents.OMINOUS_BOTTLE_AMPLIFIER 1.21 + registrator .asMutable(ItemStack.class) - .create(Keys.APPLICABLE_POTION_EFFECTS) + .create(Keys.BURN_TIME) .get(h -> { - if (h.has(DataComponents.FOOD)) { - final var itemEffects = h.get(DataComponents.FOOD).effects(); - final WeightedTable effects = new WeightedTable<>(); - final ChanceTable chance = new ChanceTable<>(); - for (final var effect : itemEffects) { - chance.add((PotionEffect) effect.effect(), effect.probability()); - } - effects.add(new NestedTableEntry<>(1, chance)); + if (SpongeCommon.game().isServerAvailable()) { + return SpongeCommon.server().fuelValues().burnDuration(h); + } else { + return null; + } + }) + .create(Keys.CONTAINER_ITEM) + .get(h -> (ItemType) h.getItem().getCraftingRemainder().item()) + .create(Keys.DISPLAY_NAME) + .get(h -> SpongeAdventure.asAdventure(h.getDisplayName())) + .create(Keys.CUSTOM_MODEL_DATA_FLOATS) + .get(h -> List.copyOf(h.getOrDefault(DataComponents.CUSTOM_MODEL_DATA, CustomModelData.EMPTY).floats())) + .set((h, v) -> { + final CustomModelData current = h.getOrDefault(DataComponents.CUSTOM_MODEL_DATA, CustomModelData.EMPTY); + h.set(DataComponents.CUSTOM_MODEL_DATA, new CustomModelData(List.copyOf(v), current.flags(), current.strings(), current.colors())); + }) + .create(Keys.CUSTOM_MODEL_DATA_FLAGS) + .get(h -> List.copyOf(h.getOrDefault(DataComponents.CUSTOM_MODEL_DATA, CustomModelData.EMPTY).flags())) + .set((h, v) -> { + final CustomModelData current = h.getOrDefault(DataComponents.CUSTOM_MODEL_DATA, CustomModelData.EMPTY); - return effects; - } - return null; + h.set(DataComponents.CUSTOM_MODEL_DATA, new CustomModelData(current.floats(), List.copyOf(v), current.strings(), current.colors())); }) + .create(Keys.CUSTOM_MODEL_DATA_STRINGS) + .get(h -> List.copyOf(h.getOrDefault(DataComponents.CUSTOM_MODEL_DATA, CustomModelData.EMPTY).strings())) .set((h, v) -> { - List newEffects = new ArrayList<>(); - for (final TableEntry entry : v.entries()) { - if (entry instanceof NestedTableEntry nestedTableEntry) { - if (nestedTableEntry.getNestedTable() instanceof ChanceTable chanceTable) { - for (final TableEntry te : chanceTable.entries()) { - if (te instanceof WeightedObject wo) { - newEffects.add(new FoodProperties.PossibleEffect((MobEffectInstance) wo.get(), (float) te.weight())); - } - } - } - } - } - h.update(DataComponents.FOOD, DEFAULT_FOOD_PROPERTIES, - fp -> new FoodProperties(fp.nutrition(), fp.saturation(), fp.canAlwaysEat(), fp.eatSeconds(), fp.usingConvertsTo(), newEffects)); + final CustomModelData current = h.getOrDefault(DataComponents.CUSTOM_MODEL_DATA, CustomModelData.EMPTY); + + h.set(DataComponents.CUSTOM_MODEL_DATA, new CustomModelData(current.floats(), current.flags(), List.copyOf(v), current.colors())); }) + .create(Keys.CUSTOM_MODEL_DATA_COLORS) + .get(h -> h.getOrDefault(DataComponents.CUSTOM_MODEL_DATA, CustomModelData.EMPTY).colors().stream() + .map(Color::ofRgb) + .toList()) + .set((h, v) -> { + final CustomModelData current = h.getOrDefault(DataComponents.CUSTOM_MODEL_DATA, CustomModelData.EMPTY); - .create(Keys.BURN_TIME) - .get(h -> { - final Integer burnTime = AbstractFurnaceBlockEntity.getFuel().get(h.getItem()); - if (burnTime != null && burnTime > 0) { - return burnTime; - } - return null; + h.set(DataComponents.CUSTOM_MODEL_DATA, new CustomModelData(current.floats(), current.flags(), current.strings(), v.stream() + .map(Color::rgb) + .toList())); }) - .create(Keys.CONTAINER_ITEM) - .get(h -> (ItemType) h.getItem().getCraftingRemainingItem()) - .create(Keys.DISPLAY_NAME) - .get(h -> SpongeAdventure.asAdventure(h.getDisplayName())) - .create(Keys.CUSTOM_MODEL_DATA) - .get(h -> h.getOrDefault(DataComponents.CUSTOM_MODEL_DATA, CustomModelData.DEFAULT).value()) - .set((h, v) -> h.set(DataComponents.CUSTOM_MODEL_DATA, new CustomModelData(v))) - .delete(h -> h.remove(DataComponents.CUSTOM_MODEL_DATA)) .create(Keys.CUSTOM_NAME) .get(h -> { if (h.has(DataComponents.CUSTOM_NAME)) { @@ -204,6 +211,9 @@ public static void register(final DataProviderRegistrator registrator) { return true; }) .delete(h -> h.remove(DataComponents.MAX_STACK_SIZE)) + .create(Keys.MODEL) + .get(stack -> (ResourceKey) (Object) stack.get(DataComponents.ITEM_MODEL)) + .set((stack, model) -> stack.set(DataComponents.ITEM_MODEL, (Identifier) (Object) model)) .create(Keys.ITEM_DURABILITY) .get(stack -> stack.getMaxDamage() - stack.getDamageValue()) .set((stack, durability) -> stack.setDamageValue(stack.getMaxDamage() - durability)) @@ -217,37 +227,49 @@ public static void register(final DataProviderRegistrator registrator) { return food == null ? null : food.nutrition(); }) .set((h, v) -> h.update(DataComponents.FOOD, DEFAULT_FOOD_PROPERTIES, - fp -> new FoodProperties(v, fp.saturation(), fp.canAlwaysEat(), fp.eatSeconds(), fp.usingConvertsTo(), fp.effects()))) + fp -> new FoodProperties(v, fp.saturation(), fp.canAlwaysEat()))) .create(Keys.REPLENISHED_SATURATION) .get(h -> { final var food = h.get(DataComponents.FOOD); return food == null ? null : (double) food.saturation(); }) .set((h, v) -> h.update(DataComponents.FOOD, DEFAULT_FOOD_PROPERTIES, - fp -> new FoodProperties(fp.nutrition(), v.floatValue(), fp.canAlwaysEat(), fp.eatSeconds(), fp.usingConvertsTo(), fp.effects()))) + fp -> new FoodProperties(fp.nutrition(), v.floatValue(), fp.canAlwaysEat()))) .create(Keys.CAN_ALWAYS_EAT) .get(h -> { final var food = h.get(DataComponents.FOOD); return food == null ? null : food.canAlwaysEat(); }) .set((h, v) -> h.update(DataComponents.FOOD, DEFAULT_FOOD_PROPERTIES, - fp -> new FoodProperties(fp.nutrition(), fp.saturation(), v, fp.eatSeconds(), fp.usingConvertsTo(), fp.effects()))) + fp -> new FoodProperties(fp.nutrition(), fp.saturation(), v))) .create(Keys.EATING_TIME) .get(h -> { - final var food = h.get(DataComponents.FOOD); - return food == null ? null : Ticks.of(food.eatDurationTicks()); + final var consumable = h.get(DataComponents.CONSUMABLE); + return consumable == null ? null : Ticks.of(consumable.consumeTicks()); }) - .set((h, v) -> h.update(DataComponents.FOOD, DEFAULT_FOOD_PROPERTIES, - fp -> new FoodProperties(fp.nutrition(), fp.saturation(), fp.canAlwaysEat(), v.ticks() / 20f, fp.usingConvertsTo(), fp.effects()))) + .set((h, v) -> h.update(DataComponents.CONSUMABLE, DEFAULT_CONSUMABLE_PROPERTIES, + c -> new Consumable(v.ticks() / 20f, c.animation(), c.sound(), c.hasConsumeParticles(), c.onConsumeEffects()))) + .create(Keys.CONSUME_ACTIONS) + .get(h -> { + final var consumable = h.get(DataComponents.CONSUMABLE); + return consumable == null ? null : (List) (Object) consumable.onConsumeEffects(); + }) + .set((h, v) -> h.update(DataComponents.CONSUMABLE, DEFAULT_CONSUMABLE_PROPERTIES, + c -> new Consumable(c.consumeSeconds(), c.animation(), c.sound(), c.hasConsumeParticles(), (List) (Object) v))) + .create(Keys.DEATH_PROTECTION_ACTIONS) + .get(h -> { + final var deathProtection = h.get(DataComponents.DEATH_PROTECTION); + return deathProtection == null ? null : (List) (Object) deathProtection.deathEffects(); + }) + .set((h, v) -> h.set(DataComponents.DEATH_PROTECTION, new DeathProtection((List) (Object) v))) + .delete(h -> h.remove(DataComponents.DEATH_PROTECTION)) .create(Keys.FOOD_CONVERTS_TO) .get(h -> { - final var food = h.get(DataComponents.FOOD); - return food == null ? null : food.usingConvertsTo().map(ItemStackUtil::fromNative).orElse(null); + final var remainder = h.get(DataComponents.USE_REMAINDER); + return remainder == null ? null : ItemStackUtil.fromTemplate(remainder.convertInto()); }) - .set((h, v) -> h.update(DataComponents.FOOD, DEFAULT_FOOD_PROPERTIES, - fp -> new FoodProperties(fp.nutrition(), fp.saturation(), fp.canAlwaysEat(), fp.eatSeconds(), Optional.ofNullable(ItemStackUtil.toNative(v)), fp.effects()))) - .delete(h -> h.update(DataComponents.FOOD, DEFAULT_FOOD_PROPERTIES, - fp -> new FoodProperties(fp.nutrition(), fp.saturation(), fp.canAlwaysEat(), fp.eatSeconds(), Optional.empty(), fp.effects()))) + .set((h, v) -> h.set(DataComponents.USE_REMAINDER, new UseRemainder(ItemStackUtil.toTemplate(v)))) + .delete(h -> h.remove(DataComponents.USE_REMAINDER)) .create(Keys.REPAIR_COST) .get(h -> h.getOrDefault(DataComponents.REPAIR_COST, 0)) .set((stack, cost) -> stack.set(DataComponents.REPAIR_COST, cost)) @@ -257,8 +279,8 @@ public static void register(final DataProviderRegistrator registrator) { .set((stack, value) -> stack.set(DataComponents.ENCHANTMENT_GLINT_OVERRIDE, value)) .delete(stack -> stack.remove(DataComponents.ENCHANTMENT_GLINT_OVERRIDE)) .create(Keys.CHARGED_PROJECTILES) - .get(h -> ItemStackUtil.snapshotOf(h.getOrDefault(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.EMPTY).getItems())) - .set((stack, value) -> stack.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.of(ItemStackUtil.fromSnapshotToNativeList(value)))) + .get(h -> ItemStackUtil.snapshotOf(h.getOrDefault(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.EMPTY).items())) + .set((stack, value) -> stack.set(DataComponents.CHARGED_PROJECTILES, new ChargedProjectiles(ItemStackUtil.toTemplate(value)))) .delete(stack -> stack.remove(DataComponents.CHARGED_PROJECTILES)) .create(Keys.INTANGIBLE_PROJECTILE) .get(h -> h.get(DataComponents.INTANGIBLE_PROJECTILE) != null) @@ -278,18 +300,30 @@ public static void register(final DataProviderRegistrator registrator) { }) .delete(stack -> stack.remove(DataComponents.CONTAINER)) .create(Keys.FIRE_RESISTANT) - .get(h -> h.get(DataComponents.FIRE_RESISTANT) != null) + .get(h -> { + final @Nullable DamageResistant resist = h.get(DataComponents.DAMAGE_RESISTANT); + if (resist == null) { + return false; + } + if (resist.types() instanceof net.minecraft.core.HolderSet.Named named) { + return named.key().location().equals(DamageTypes.IN_FIRE.identifier()); + } + return false; + }) .set((h, value) -> { if (value) { - h.set(DataComponents.FIRE_RESISTANT, Unit.INSTANCE); + final var holderSet = SpongeCommon.vanillaRegistry(net.minecraft.core.registries.Registries.DAMAGE_TYPE).getOrThrow(DamageTypeTags.IS_FIRE); + h.applyComponents(DataComponentPatch.builder() + .set(DataComponents.DAMAGE_RESISTANT, new DamageResistant(holderSet)) + .build()); } else { - h.remove(DataComponents.FIRE_RESISTANT); + h.remove(DataComponents.DAMAGE_RESISTANT); } }) - .delete(stack -> stack.remove(DataComponents.FIRE_RESISTANT)) + .delete(stack -> stack.remove(DataComponents.DAMAGE_RESISTANT)) .create(Keys.ITEM_NAME) .get(h -> { - final Component component = h.get(DataComponents.ITEM_NAME); + final @Nullable Component component = h.get(DataComponents.ITEM_NAME); if (component == null) { return null; } @@ -297,6 +331,120 @@ public static void register(final DataProviderRegistrator registrator) { }) .set((h, value) -> h.set(DataComponents.ITEM_NAME, SpongeAdventure.asVanillaMutable(value))) .delete(stack -> stack.remove(DataComponents.ITEM_NAME)) + .create(Keys.COOLDOWN_GROUP) + .get(h -> { + final @Nullable UseCooldown cooldown = h.get(DataComponents.USE_COOLDOWN); + if (cooldown == null) { + return null; + } + return (ResourceKey) (Object) cooldown.cooldownGroup().orElse(null); + }) + .set((h, value) -> { + if (value == null) { + h.remove(DataComponents.USE_COOLDOWN); + return; + } + h.set(DataComponents.USE_COOLDOWN, new UseCooldown(1, Optional.of((Identifier) (Object) value))); + }) + .deleteAndGet(ItemStackData::deleteAndTransactUseCooldown) + .create(Keys.COOLDOWN) + .get(h -> { + final @Nullable UseCooldown cooldown = h.get(DataComponents.USE_COOLDOWN); + if (cooldown == null) { + return null; + } + return Ticks.of(cooldown.ticks()); + }) + .setAndGet((h, value) -> { + if (value == null) { + return ItemStackData.deleteAndTransactUseCooldown(h); + } + final var existing = h.get(DataComponents.USE_COOLDOWN); + var builder = DataTransactionResult.builder() + .success(Value.immutableOf(Keys.COOLDOWN, value)); + if (existing != null) { + h.set(DataComponents.USE_COOLDOWN, new UseCooldown(value.ticks(), existing.cooldownGroup())); + builder.replace(existing.cooldownGroup().map(ResourceKey.class::cast) + .map(group -> + List.of( + Value.immutableOf(Keys.COOLDOWN, Ticks.of(existing.ticks())), + Value.immutableOf(Keys.COOLDOWN_GROUP, group) + ) + ) + .orElseGet(() -> List.of( + Value.immutableOf(Keys.COOLDOWN, Ticks.of(existing.ticks())) + ))); + } else { + h.set(DataComponents.USE_COOLDOWN, new UseCooldown(value.ticks(), Optional.empty())); + } + return builder.build(); + }) + .deleteAndGet(ItemStackData::deleteAndTransactUseCooldown) + .create(Keys.TOOLTIP_STYLE) + .get(h -> (ResourceKey) (Object) h.get(DataComponents.TOOLTIP_STYLE)) + .set((h, v) -> h.set(DataComponents.TOOLTIP_STYLE, (Identifier) (Object) v)) + .delete(h -> h.remove(DataComponents.TOOLTIP_STYLE)) + .create(Keys.WEAPON_DAMAGE_PER_ATTACK) + .get(h -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + if (weapon == null) { + return null; + } + return weapon.itemDamagePerAttack(); + }) + .set((h, v) -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + h.set(DataComponents.WEAPON, new Weapon(v, weapon == null ? 0 : weapon.disableBlockingForSeconds())); + }) + .delete(h -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + if (weapon == null) { + return; + } + if (weapon.disableBlockingForSeconds() == 0) { + h.remove(DataComponents.WEAPON); + } else { + h.set(DataComponents.WEAPON, new Weapon(0, weapon.disableBlockingForSeconds())); + } + }) + .create(Keys.DISABLE_SHIELD_TICKS) + .get(h -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + if (weapon == null) { + return null; + } + return Ticks.of(Math.round( + Constants.TickConversions.TICKS_PER_SECOND * weapon.disableBlockingForSeconds() + )); + }) + .set((h, v) -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + h.set(DataComponents.WEAPON, new Weapon( + weapon == null ? 0 : weapon.itemDamagePerAttack(), + v.ticks() / (float) Constants.TickConversions.TICKS_PER_SECOND + )); + }) + .delete(h -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + if (weapon == null) { + return; + } + if (weapon.itemDamagePerAttack() == 0) { + h.remove(DataComponents.WEAPON); + } else { + h.set(DataComponents.WEAPON, new Weapon(weapon.itemDamagePerAttack())); + } + }) + .create(Keys.ITEM_ATTRIBUTES) + .get(stack -> { + final var attributes = stack.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY).modifiers(); + return attributes.isEmpty() ? null : (List) (Object) List.copyOf(attributes); + }) + .set((stack, v) -> { + final var attributes = (List) (Object) List.copyOf(v); + stack.set(DataComponents.ATTRIBUTE_MODIFIERS, new ItemAttributeModifiers(attributes)); + }) + .resetOnDelete(List.of()) .create(Keys.BLOCK_STATE) .get(stack -> { if (!(stack.getItem() instanceof final BlockItem block)) { @@ -331,7 +479,7 @@ private static Inventory inventoryFromItemContainerContents(final ItemContainerC if (contents == null) { return null; } - var slots = contents.stream().map(ItemStackUtil::cloneDefensive).toList(); + var slots = contents.allItemsCopyStream().map(ItemStackUtil::fromNative).toList(); if (slots.isEmpty()) { return null; } @@ -344,12 +492,26 @@ private static Inventory inventoryFromItemContainerContents(final ItemContainerC private static void setIsUnbrekable(final ItemStack stack, final Boolean value) { if (value) { - stack.set(DataComponents.UNBREAKABLE, new Unbreakable(true)); + stack.set(DataComponents.UNBREAKABLE, Unit.INSTANCE); } else { stack.remove(DataComponents.UNBREAKABLE); } } + private static DataTransactionResult deleteAndTransactUseCooldown(final ItemStack stack) { + final @Nullable UseCooldown cooldown = stack.remove(DataComponents.USE_COOLDOWN); + if (cooldown == null) { + return DataTransactionResult.successNoData(); + } + return cooldown.cooldownGroup() + .map(group -> DataTransactionResult.successRemove(List.of( + Value.immutableOf(Keys.COOLDOWN, Ticks.of(cooldown.ticks())), + Value.immutableOf(Keys.COOLDOWN_GROUP, (ResourceKey) (Object) group) + ))).orElseGet(() -> DataTransactionResult.successRemove(List.of( + Value.immutableOf(Keys.COOLDOWN, Ticks.of(cooldown.ticks())) + ))); + } + private static > Optional propertyValue(final net.minecraft.world.level.block.state.BlockState blockState, final Property property) { return blockState.getOptionalValue(property).map(property::getName); } diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/PotionItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/PotionItemStackData.java index 8a5a4d2f6a4..77f83375f87 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/PotionItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/PotionItemStackData.java @@ -30,6 +30,7 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.item.alchemy.Potion; import net.minecraft.world.item.alchemy.PotionContents; import org.spongepowered.api.data.Keys; @@ -55,8 +56,9 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(ItemStack.class) .create(Keys.COLOR) .get(h -> Color.ofRgb(h.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY).getColor())) - .set((h, v) -> h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(contents.potion(), Optional.of(v.rgb()), contents.customEffects()))) - .delete(h -> h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(contents.potion(), Optional.empty(), contents.customEffects()))) + .set((h, v) -> h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(contents.potion(), Optional.of(v.rgb()), contents.customEffects(), contents.customName()))) + .delete(h -> h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(contents.potion(), Optional.empty(), contents.customEffects(), contents.customName()))) + .supports(h -> h.getItem() == Items.POTION || h.getItem() == Items.SPLASH_POTION || h.getItem() == Items.LINGERING_POTION || h.getItem() == Items.TIPPED_ARROW) .create(Keys.CUSTOM_POTION_EFFECTS) .get(h -> { final List effects = h.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY).customEffects(); @@ -64,16 +66,16 @@ public static void register(final DataProviderRegistrator registrator) { }) .set((h, v) -> { final var mcList = v.stream().map(MobEffectInstance.class::cast).toList(); - h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(contents.potion(), contents.customColor(), mcList)); + h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(contents.potion(), contents.customColor(), mcList, contents.customName())); }) - .delete(h -> h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(contents.potion(), contents.customColor(), Collections.emptyList()))) + .delete(h -> h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(contents.potion(), contents.customColor(), Collections.emptyList(), contents.customName()))) .create(Keys.POTION_TYPE) .get(h -> (PotionType) h.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY).potion().map(Holder::value).orElse(null)) // TODO empty POTION gone? .set((h, v) -> { final var potion = Optional.ofNullable(v).map(Potion.class::cast).map(BuiltInRegistries.POTION::wrapAsHolder); // TODO set empty POTION? same as delete? - h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(potion, contents.customColor(), contents.customEffects())); + h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(potion, contents.customColor(), contents.customEffects(), contents.customName())); }) - .delete(h -> h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(Optional.empty(), contents.customColor(), contents.customEffects()))) + .delete(h -> h.update(DataComponents.POTION_CONTENTS, PotionContents.EMPTY, contents -> new PotionContents(Optional.empty(), contents.customColor(), contents.customEffects(), contents.customName()))) .create(Keys.POTION_EFFECTS) .get(h -> { final Iterable effects = h.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY).getAllEffects(); diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/ShieldItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/ShieldItemStackData.java index 9b499f8084f..cdc9a6da610 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/ShieldItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/ShieldItemStackData.java @@ -24,15 +24,31 @@ */ package org.spongepowered.common.data.provider.item.stack; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; import net.minecraft.core.component.DataComponents; -import net.minecraft.world.item.BannerItem; +import net.minecraft.core.registries.Registries; +import net.minecraft.sounds.SoundEvent; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.ShieldItem; +import net.minecraft.world.item.component.BlocksAttacks; import net.minecraft.world.level.block.entity.BannerPatternLayers; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.meta.BannerPatternLayer; import org.spongepowered.api.data.type.DyeColor; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; +import org.spongepowered.api.effect.sound.SoundType; +import org.spongepowered.api.event.cause.entity.damage.DamageType; +import org.spongepowered.api.tag.Tag; +import org.spongepowered.api.util.Ticks; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.bridge.tags.TagBridge; import org.spongepowered.common.data.provider.DataProviderRegistrator; +import org.spongepowered.common.util.Constants; + +import java.util.List; +import java.util.Optional; public final class ShieldItemStackData { @@ -46,7 +62,6 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.DYE_COLOR) .get(h -> (DyeColor) (Object) h.getOrDefault(DataComponents.BASE_COLOR, net.minecraft.world.item.DyeColor.WHITE)) .set((h, v) -> h.set(DataComponents.BASE_COLOR, (net.minecraft.world.item.DyeColor) (Object) v)) - .supports(h -> h.getItem() instanceof ShieldItem) .create(Keys.BANNER_PATTERN_LAYERS) .get(h -> h.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY).layers() .stream().map(BannerPatternLayer.class::cast).toList()) @@ -54,8 +69,200 @@ public static void register(final DataProviderRegistrator registrator) { h.set(DataComponents.BANNER_PATTERNS, new BannerPatternLayers(v.stream().map(BannerPatternLayers.Layer.class::cast).toList())); // TODO check setting banner base? Constants.TileEntity.Banner.BANNER_BASE / BannerPatternShapes.BASE }) - .supports(h -> h.getItem() instanceof ShieldItem || h.getItem() instanceof BannerItem); + .create(Keys.SHIELD_DEPLOY_TICKS) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null) { + return null; + } + return Ticks.of((long) (Constants.TickConversions.TICKS_PER_SECOND * blocksAttacks.blockDelaySeconds())); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + v.ticks() / (float) Constants.TickConversions.TICKS_PER_SECOND, + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.DISABLE_SHIELD_TICKS_SCALE) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null) { + return null; + } + return (double) blocksAttacks.disableCooldownScale(); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + v.floatValue(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.SHIELD_DAMAGE_REDUCTIONS) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null) { + return null; + } + return (List>) (Object) List.copyOf(blocksAttacks.damageReductions()); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + (List) (Object) List.copyOf(v), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.SHIELD_ITEM_DAMAGE_FUNCTION) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null) { + return null; + } + return (ShieldItemDamageFunction) (Object) blocksAttacks.itemDamage(); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + (BlocksAttacks.ItemDamageFunction) (Object) v, + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.BYPASS_DAMAGE_TAG) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null || blocksAttacks.bypassedBy().isEmpty()) { + return null; + } + final HolderSet holderSet = blocksAttacks.bypassedBy().get(); + if (holderSet instanceof HolderSet.Named named) { + return (Tag) (Object) named.key(); + } + return null; + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + final var tagKey = ((TagBridge) v).bridge$asVanillaTag(); + final var holderSet = SpongeCommon.vanillaRegistry(Registries.DAMAGE_TYPE).getOrThrow(tagKey); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + Optional.of(holderSet), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .delete(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + Optional.empty(), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.SHIELD_BLOCK_SOUND) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null || blocksAttacks.blockSound().isEmpty()) { + return null; + } + return (SoundType) (Object) blocksAttacks.blockSound().get().value(); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + Optional.of(Holder.direct((SoundEvent) (Object) v)), + blocksAttacks.disableSound() + )); + }) + .delete(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + Optional.empty(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.SHIELD_DISABLE_SOUND) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null || blocksAttacks.disableSound().isEmpty()) { + return null; + } + return (SoundType) (Object) blocksAttacks.disableSound().get().value(); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + Optional.of(Holder.direct((SoundEvent) (Object) v)) + )); + }) + .delete(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + Optional.empty() + )); + }) + ; } // @formatter:on + private static final BlocksAttacks BLOCKS_ATTACKS_DEFAULTS = new BlocksAttacks( + 0, + 1, + List.of(), + BlocksAttacks.ItemDamageFunction.DEFAULT, + Optional.empty(), + Optional.empty(), + Optional.empty() + ); + } diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/SignItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/SignItemStackData.java index dc66937f7e0..2bad6745879 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/SignItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/SignItemStackData.java @@ -30,19 +30,20 @@ import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.item.component.TypedEntityData; +import net.minecraft.world.level.block.entity.BlockEntityTypes; import net.minecraft.world.level.block.entity.SignText; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.util.Constants; -import java.util.List; +import java.util.Arrays; public final class SignItemStackData { - private SignItemStackData() { } @@ -52,28 +53,21 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(ItemStack.class) .create(Keys.SIGN_LINES) .get(h -> { - final CompoundTag tag = h.getOrDefault(DataComponents.BLOCK_ENTITY_DATA, CustomData.EMPTY).copyTag(); - if (tag.isEmpty()) { + final @Nullable TypedEntityData data = h.get(DataComponents.BLOCK_ENTITY_DATA); + if (data == null) { return null; } - final String id = tag.getString(Constants.Item.BLOCK_ENTITY_ID); + final var tag = data.getUnsafe(); + + final String id = tag.getStringOr(Constants.Item.BLOCK_ENTITY_ID, ""); if (!id.equalsIgnoreCase(Constants.TileEntity.SIGN)) { return null; } - DynamicOps $$2 = SpongeCommon.vanillaRegistryAccess().createSerializationContext(NbtOps.INSTANCE); - - if (tag.contains("front_text")) { - return SignText.DIRECT_CODEC - .parse($$2, tag.getCompound("front_text")) - .resultOrPartial() - .map(t -> t.getMessages(false)) - .map(List::of) - .stream() - .flatMap(List::stream) - .map(SpongeAdventure::asAdventure) - .toList(); - } - return List.of(); + return tag.read("front_text", SignText.DIRECT_CODEC).map(t -> t.getMessages(false)) + .stream() + .flatMap(Arrays::stream) + .map(SpongeAdventure::asAdventure) + .toList(); }) .set((h, v) -> { final CompoundTag tag = new CompoundTag(); @@ -94,7 +88,7 @@ public static void register(final DataProviderRegistrator registrator) { .resultOrPartial(SpongeCommon.logger()::error) .ifPresent($$1x -> tag.put("front_text", $$1x)); - h.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag)); + h.set(DataComponents.BLOCK_ENTITY_DATA, TypedEntityData.of(BlockEntityTypes.SIGN, tag)); }) .delete(h -> h.remove(DataComponents.BLOCK_ENTITY_DATA)); } diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/SkullItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/SkullItemStackData.java index 77af4878013..8b9fb1255d0 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/SkullItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/SkullItemStackData.java @@ -25,7 +25,7 @@ package org.spongepowered.common.data.provider.item.stack; import net.minecraft.core.component.DataComponents; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.PlayerHeadItem; import net.minecraft.world.item.component.ResolvableProfile; @@ -46,17 +46,17 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.GAME_PROFILE) .get(h -> { final ResolvableProfile resolvableProfile = h.get(DataComponents.PROFILE); - return resolvableProfile == null ? null : SpongeGameProfile.of(resolvableProfile.gameProfile()); + return resolvableProfile == null ? null : SpongeGameProfile.of(resolvableProfile.partialProfile()); }) .set((h, v) -> { final com.mojang.authlib.GameProfile mcProfile = SpongeGameProfile.toMcProfile(v); - h.set(DataComponents.PROFILE, new ResolvableProfile(mcProfile)); + h.set(DataComponents.PROFILE, ResolvableProfile.createResolved(mcProfile)); }) .delete(h -> h.remove(DataComponents.PROFILE)) .supports(h -> h.getItem() instanceof PlayerHeadItem) .create(Keys.NOTE_BLOCK_SOUND) .get(h -> (ResourceKey) (Object) h.get(DataComponents.NOTE_BLOCK_SOUND)) - .set((h, v) -> h.set(DataComponents.NOTE_BLOCK_SOUND, (ResourceLocation) (Object) v)) + .set((h, v) -> h.set(DataComponents.NOTE_BLOCK_SOUND, (Identifier) (Object) v)) .delete(h -> h.remove(DataComponents.NOTE_BLOCK_SOUND)) ; } diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/SpawnEggItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/SpawnEggItemStackData.java index 622f02e98f2..feb0d770768 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/SpawnEggItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/SpawnEggItemStackData.java @@ -25,11 +25,13 @@ package org.spongepowered.common.data.provider.item.stack; import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.SpawnEggItem; -import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.item.component.TypedEntityData; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.EntityArchetype; import org.spongepowered.common.data.provider.DataProviderRegistrator; @@ -38,7 +40,7 @@ public final class SpawnEggItemStackData { // @formatter:off - @SuppressWarnings({"rawTypes", "rawtypes"}) + @SuppressWarnings({"rawTypes", "rawtypes", "deprecation"}) public static void register(final DataProviderRegistrator registrator) { registrator.asMutable(ItemStack.class) .create(Keys.ENTITY_TO_SPAWN) @@ -46,11 +48,14 @@ public static void register(final DataProviderRegistrator registrator) { .get(stack -> { final Item item = stack.getItem(); final SpawnEggItem eggItem = (SpawnEggItem) item; - final EntityType type = eggItem.getType(stack); - final var tag = stack.getOrDefault(DataComponents.ENTITY_DATA, CustomData.EMPTY).getUnsafe(); + final @Nullable EntityType type = eggItem.getType(stack); + if (type == null) { + return null; + } + final var data = stack.getOrDefault(DataComponents.ENTITY_DATA, TypedEntityData.of(type, new CompoundTag())); final EntityArchetype.Builder builder = EntityArchetype.builder().type((org.spongepowered.api.entity.EntityType) type); - ((SpongeEntityArchetypeBuilder)builder).entityData(tag); + ((SpongeEntityArchetypeBuilder)builder).entityData(data.copyTagWithoutId()); return builder.build(); }) diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/TieredItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/TieredItemStackData.java index 63321580580..f6a67c892ee 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/TieredItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/TieredItemStackData.java @@ -24,12 +24,8 @@ */ package org.spongepowered.common.data.provider.item.stack; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Tier; -import net.minecraft.world.item.TieredItem; import org.spongepowered.api.data.Keys; -import org.spongepowered.api.data.type.ItemTier; import org.spongepowered.common.data.provider.DataProviderRegistrator; public final class TieredItemStackData { @@ -41,18 +37,21 @@ private TieredItemStackData() { public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(ItemStack.class) - .create(Keys.TOOL_TYPE) - .get(h -> { - final Item item = h.getItem(); - if (item instanceof TieredItem) { - final Tier tier = ((TieredItem) item).getTier(); - if (tier instanceof ItemTier) { - return (ItemTier) tier; - } - } - return null; - }) - .supports(h -> h.getItem() instanceof TieredItem); + .create(Keys.TOOL_TYPE); + // TODO - snapshot 24w34a changes away from the enum to a record and data components +// .get(h -> { +// h.is(ItemTags.WOODEN_TOOL_MATERIALS) +// final Item item = h.getItem(); +// final Tool tool = h.get(DataComponents.TOOL); +// if (item instanceof ToolItem) { +// final Tier tier = ((TieredItem) item).getTier(); +// if (tier instanceof ItemTier) { +// return (ItemTier) tier; +// } +// } +// return null; +// }) +// .supports(h -> h.getItem() instanceof TieredItem); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/ToolItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/ToolItemStackData.java index 9d07383215f..8fa927bb9d4 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/ToolItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/ToolItemStackData.java @@ -32,6 +32,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.Tool; import net.minecraft.world.level.block.Block; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.block.BlockType; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.ToolRule; @@ -53,40 +54,40 @@ public static void register(final DataProviderRegistrator registrator) { .asMutable(ItemStack.class) .create(Keys.EFFICIENCY) .get(h -> { - final Tool tool = h.get(DataComponents.TOOL); + final @Nullable Tool tool = h.get(DataComponents.TOOL); if (tool != null) { return (double) tool.defaultMiningSpeed(); } return null; }) .set((h, v) -> { - final Tool tool = h.get(DataComponents.TOOL); + final @Nullable Tool tool = h.get(DataComponents.TOOL); if (tool != null) { - h.set(DataComponents.TOOL, new Tool(tool.rules(), v.floatValue(), tool.damagePerBlock())); + h.set(DataComponents.TOOL, new Tool(tool.rules(), v.floatValue(), tool.damagePerBlock(), tool.canDestroyBlocksInCreative())); return; } - h.set(DataComponents.TOOL, new Tool(List.of(), v.floatValue(), 1)); + h.set(DataComponents.TOOL, new Tool(List.of(), v.floatValue(), 1, true)); }) .delete(h -> h.remove(DataComponents.TOOL)) .create(Keys.TOOL_DAMAGE_PER_BLOCK) .get(h -> { - final Tool tool = h.get(DataComponents.TOOL); + final @Nullable Tool tool = h.get(DataComponents.TOOL); if (tool != null) { return tool.damagePerBlock(); } return null; }) .set((h, v) -> { - final Tool tool = h.get(DataComponents.TOOL); + final @Nullable Tool tool = h.get(DataComponents.TOOL); if (tool != null) { - h.set(DataComponents.TOOL, new Tool(tool.rules(), tool.defaultMiningSpeed(), v)); + h.set(DataComponents.TOOL, new Tool(tool.rules(), tool.defaultMiningSpeed(), v, tool.canDestroyBlocksInCreative())); return; } - h.set(DataComponents.TOOL, new Tool(List.of(), 1f, v)); + h.set(DataComponents.TOOL, new Tool(List.of(), 1f, v, true)); }) .create(Keys.TOOL_RULES) .get(h -> { - final Tool tool = h.get(DataComponents.TOOL); + final @Nullable Tool tool = h.get(DataComponents.TOOL); if (tool == null) { return null; } @@ -94,17 +95,17 @@ public static void register(final DataProviderRegistrator registrator) { }) .set((h, v) -> { var mcValue = v.stream().map(Tool.Rule.class::cast).toList(); - final Tool tool = h.get(DataComponents.TOOL); + final @Nullable Tool tool = h.get(DataComponents.TOOL); if (tool != null) { - h.set(DataComponents.TOOL, new Tool(mcValue, tool.defaultMiningSpeed(), tool.damagePerBlock())); + h.set(DataComponents.TOOL, new Tool(mcValue, tool.defaultMiningSpeed(), tool.damagePerBlock(), tool.canDestroyBlocksInCreative())); return; } - h.set(DataComponents.TOOL, new Tool(mcValue, 1f, 1)); + h.set(DataComponents.TOOL, new Tool(mcValue, 1f, 1, true)); }) .delete(h -> { - final Tool tool = h.get(DataComponents.TOOL); + final @Nullable Tool tool = h.get(DataComponents.TOOL); if (tool != null) { - h.set(DataComponents.TOOL, new Tool(List.of(), tool.defaultMiningSpeed(), tool.damagePerBlock())); + h.set(DataComponents.TOOL, new Tool(List.of(), tool.defaultMiningSpeed(), tool.damagePerBlock(), tool.canDestroyBlocksInCreative())); } }) .create(Keys.CAN_HARVEST) diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/WoolItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/WoolItemStackData.java index 1543024420f..d4b93d4693c 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/WoolItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/WoolItemStackData.java @@ -28,7 +28,6 @@ import net.minecraft.world.item.ItemStack; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.DyeColor; -import org.spongepowered.common.accessor.world.entity.animal.SheepAccessor; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.util.DyeColorUtil; @@ -43,7 +42,7 @@ public static void register(final DataProviderRegistrator registrator) { .asImmutable(ItemStack.class) .create(Keys.DYE_COLOR) .get(h -> (DyeColor) (Object) DyeColorUtil.COLOR_BY_WOOL.get(((BlockItem) h.getItem()).getBlock())) - .supports(h -> h.getItem() instanceof BlockItem && SheepAccessor.accessor$ITEM_BY_DYE().containsValue(((BlockItem) h.getItem()).getBlock())); + .supports(h -> h.getItem() instanceof BlockItem && DyeColorUtil.COLOR_BY_WOOL.containsKey(((BlockItem) h.getItem()).getBlock())); } // @formatter:on } diff --git a/src/main/java/org/spongepowered/common/data/provider/map/MapInfoData.java b/src/main/java/org/spongepowered/common/data/provider/map/MapInfoData.java index fab0ec7361e..983f9a6af8f 100644 --- a/src/main/java/org/spongepowered/common/data/provider/map/MapInfoData.java +++ b/src/main/java/org/spongepowered/common/data/provider/map/MapInfoData.java @@ -25,7 +25,7 @@ package org.spongepowered.common.data.provider.map; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.saveddata.maps.MapItemSavedData; import org.apache.logging.log4j.LogManager; import org.spongepowered.api.ResourceKey; @@ -92,10 +92,10 @@ public static void register(final DataProviderRegistrator registrator) { return null; } - return (ResourceKey) (Object) mapData.dimension.location(); + return (ResourceKey) (Object) mapData.dimension.identifier(); }) .set((mapData, key) -> { - ((MapItemSavedDataAccessor) mapData).accessor$dimension(net.minecraft.resources.ResourceKey.create(Registries.DIMENSION, (ResourceLocation) (Object) key)); + ((MapItemSavedDataAccessor) mapData).accessor$dimension(net.minecraft.resources.ResourceKey.create(Registries.DIMENSION, (Identifier) (Object) key)); mapData.setDirty(); }) .asMutable(MapItemSavedDataBridge.class) diff --git a/src/main/java/org/spongepowered/common/data/provider/world/LevelStemData.java b/src/main/java/org/spongepowered/common/data/provider/world/LevelStemData.java index b435651a4e0..70dc1e745e8 100644 --- a/src/main/java/org/spongepowered/common/data/provider/world/LevelStemData.java +++ b/src/main/java/org/spongepowered/common/data/provider/world/LevelStemData.java @@ -26,14 +26,10 @@ import net.minecraft.world.level.dimension.LevelStem; import org.spongepowered.api.data.Keys; -import org.spongepowered.api.entity.living.player.gamemode.GameMode; import org.spongepowered.api.world.WorldType; -import org.spongepowered.api.world.difficulty.Difficulty; import org.spongepowered.api.world.generation.ChunkGenerator; -import org.spongepowered.common.bridge.world.level.dimension.LevelStemBridge; import org.spongepowered.common.data.provider.DataProviderRegistrator; -// Used as delegate for WorldTemplate public final class LevelStemData { private LevelStemData() { @@ -47,31 +43,6 @@ public static void register(final DataProviderRegistrator registrator) { .get(h -> (WorldType) (Object) h.type().value()) .create(Keys.CHUNK_GENERATOR) .get(h -> (ChunkGenerator) h.generator()) - .asImmutable(LevelStemBridge.class) - .create(Keys.DISPLAY_NAME) - .get(LevelStemBridge::bridge$displayName) - .create(Keys.GAME_MODE) - .get(h -> (GameMode) (Object) h.bridge$gameMode()) - .create(Keys.WORLD_DIFFICULTY) - .get(h -> (Difficulty) (Object) h.bridge$difficulty()) - .create(Keys.SERIALIZATION_BEHAVIOR) - .get(LevelStemBridge::bridge$serializationBehavior) - .create(Keys.IS_LOAD_ON_STARTUP) - .get(LevelStemBridge::bridge$loadOnStartup) - .create(Keys.PERFORM_SPAWN_LOGIC) - .get(LevelStemBridge::bridge$performsSpawnLogic) - .create(Keys.HARDCORE) - .get(LevelStemBridge::bridge$hardcore) - .create(Keys.COMMANDS) - .get(LevelStemBridge::bridge$allowCommands) - .create(Keys.PVP) - .get(LevelStemBridge::bridge$pvp) - .create(Keys.VIEW_DISTANCE) - .get(LevelStemBridge::bridge$viewDistance) - .create(Keys.SPAWN_POSITION) - .get(LevelStemBridge::bridge$spawnPosition) - .create(Keys.SEED) - .get(LevelStemBridge::bridge$seed) ; } diff --git a/neoforge/src/main/java/org/spongepowered/neoforge/SpongeMod.java b/src/main/java/org/spongepowered/common/data/provider/world/ServerLevelDataProvider.java similarity index 91% rename from neoforge/src/main/java/org/spongepowered/neoforge/SpongeMod.java rename to src/main/java/org/spongepowered/common/data/provider/world/ServerLevelDataProvider.java index 2191386a358..7141a9c7cdc 100644 --- a/neoforge/src/main/java/org/spongepowered/neoforge/SpongeMod.java +++ b/src/main/java/org/spongepowered/common/data/provider/world/ServerLevelDataProvider.java @@ -22,10 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge; +package org.spongepowered.common.data.provider.world; -import net.neoforged.fml.common.Mod; +public class ServerLevelDataProvider { -@Mod("sponge") -public class SpongeMod { } diff --git a/src/main/java/org/spongepowered/common/data/provider/world/WorldPropertiesData.java b/src/main/java/org/spongepowered/common/data/provider/world/WorldPropertiesData.java index 9f6b4029aca..e32ba40a544 100644 --- a/src/main/java/org/spongepowered/common/data/provider/world/WorldPropertiesData.java +++ b/src/main/java/org/spongepowered/common/data/provider/world/WorldPropertiesData.java @@ -24,8 +24,10 @@ */ package org.spongepowered.common.data.provider.world; +import net.minecraft.core.GlobalPos; import net.minecraft.world.level.GameType; import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.storage.LevelData; import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.storage.ServerLevelData; @@ -34,9 +36,10 @@ import org.spongepowered.api.entity.living.player.gamemode.GameMode; import org.spongepowered.api.world.SerializationBehavior; import org.spongepowered.api.world.WorldType; -import org.spongepowered.api.world.border.WorldBorder; import org.spongepowered.api.world.difficulty.Difficulty; import org.spongepowered.api.world.generation.config.WorldGenerationConfig; +import org.spongepowered.api.world.server.WorldArchetype; +import org.spongepowered.api.world.server.WorldArchetypeType; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.world.level.storage.PrimaryLevelDataBridge; import org.spongepowered.common.bridge.world.level.storage.ServerLevelDataBridge; @@ -54,15 +57,19 @@ public static void register(final DataProviderRegistrator registrator) { registrator .asMutable(LevelData.class) .create(Keys.SPAWN_POSITION) - .get(h -> VecHelper.toVector3i(h.getSpawnPos())) + .get(h -> VecHelper.toVector3i(h.getRespawnData().pos())) .create(Keys.HARDCORE) .get(LevelData::isHardcore) .create(Keys.WORLD_DIFFICULTY) .get(h -> (Difficulty) (Object) h.getDifficulty()) .asMutable(WritableLevelData.class) .create(Keys.SPAWN_POSITION) - .get(h -> VecHelper.toVector3i(h.getSpawnPos())) - .set((h, v) -> h.setSpawn(VecHelper.toBlockPos(v), h.getSpawnAngle())) + .get(h -> VecHelper.toVector3i(h.getRespawnData().pos())) + .set((h, v) -> h.setSpawn( + new LevelData.RespawnData(new GlobalPos( + h.getRespawnData().dimension(), + VecHelper.toBlockPos(v) + ), h.getRespawnData().pitch(), h.getRespawnData().yaw()))) .asMutable(ServerLevelData.class) .create(Keys.GAME_MODE) .get(h -> (GameMode) (Object) h.getGameType()) @@ -71,16 +78,11 @@ public static void register(final DataProviderRegistrator registrator) { .get(ServerLevelData::isAllowCommands) .create(Keys.INITIALIZED) .get(ServerLevelData::isInitialized) - .create(Keys.WORLD_BORDER) - .get(h -> (WorldBorder) h.getWorldBorder()) - .create(Keys.WEATHER) - .get(SpongeWeather::of) - .set(SpongeWeather::apply) .asMutable(ServerLevelDataBridge.class) .create(Keys.WORLD_TYPE) .get(h -> (WorldType) (Object) h.bridge$dimensionType()) .create(Keys.PVP) - .get(h -> h.bridge$pvp().orElseGet(() -> SpongeCommon.server().isPvpAllowed())) + .get(h -> h.bridge$pvp().orElseGet(() -> SpongeCommon.server().getGameRules().get(GameRules.PVP))) .create(Keys.SERIALIZATION_BEHAVIOR) .get(h -> h.bridge$serializationBehavior().orElse(SerializationBehavior.AUTOMATIC)) .create(Keys.VIEW_DISTANCE) @@ -91,11 +93,13 @@ public static void register(final DataProviderRegistrator registrator) { .get(ServerLevelDataBridge::bridge$performsSpawnLogic) .create(Keys.IS_LOAD_ON_STARTUP) .get(ServerLevelDataBridge::bridge$loadOnStartup) + .create(Keys.WORLD_ARCHETYPE_TYPE) + .get(h -> (WorldArchetypeType) (Object) h.bridge$levelStem()) + .create(Keys.WORLD_BORDER) + .get(h -> h.bridge$worldBorder().bridge$asImmutable()) .asMutable(PrimaryLevelData.class) .create(Keys.WORLD_DIFFICULTY) .set((h, v) -> h.setDifficulty((net.minecraft.world.Difficulty) (Object) v)) - .create(Keys.WORLD_GEN_CONFIG) - .get(h -> (WorldGenerationConfig) h.worldGenOptions()) .asMutable(PrimaryLevelDataBridge.class) .create(Keys.WORLD_TYPE) .set((h, v) -> h.bridge$dimensionType((DimensionType) (Object) v, true)) @@ -115,6 +119,14 @@ public static void register(final DataProviderRegistrator registrator) { .set(PrimaryLevelDataBridge::bridge$setPerformsSpawnLogic) .create(Keys.IS_LOAD_ON_STARTUP) .set(PrimaryLevelDataBridge::bridge$setLoadOnStartup) + .create(Keys.WORLD_ARCHETYPE) + .get(h -> WorldArchetype.of((WorldArchetypeType) (Object) h.bridge$levelStem(), (WorldGenerationConfig) h.bridge$spongeData().worldGenOptions())) + .create(Keys.WORLD_GEN_CONFIG) + .get(h -> (WorldGenerationConfig) h.bridge$spongeData().worldGenOptions()) + .asMutable(ServerLevelDataBridge.class) + .create(Keys.WEATHER) + .get(h -> SpongeWeather.of(h.bridge$level().getWeatherData())) + .set(SpongeWeather::apply) ; } // @formatter:on diff --git a/src/main/java/org/spongepowered/common/data/provider/world/WorldTypeData.java b/src/main/java/org/spongepowered/common/data/provider/world/WorldTypeData.java index 70b27307881..f520c45e1fd 100644 --- a/src/main/java/org/spongepowered/common/data/provider/world/WorldTypeData.java +++ b/src/main/java/org/spongepowered/common/data/provider/world/WorldTypeData.java @@ -24,23 +24,22 @@ */ package org.spongepowered.common.data.provider.world; +import net.minecraft.core.HolderSet; import net.minecraft.util.valueproviders.IntProvider; +import net.minecraft.world.attribute.BedRule; +import net.minecraft.world.attribute.EnvironmentAttributes; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.dimension.DimensionType; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.ResourceKey; import org.spongepowered.api.block.BlockType; import org.spongepowered.api.data.Keys; import org.spongepowered.api.tag.Tag; -import org.spongepowered.api.util.MinecraftDayTime; import org.spongepowered.api.util.Range; import org.spongepowered.api.world.WorldTypeEffect; import org.spongepowered.common.accessor.world.level.dimension.DimensionTypeAccessor; import org.spongepowered.common.bridge.world.level.dimension.DimensionTypeBridge; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.registry.provider.DimensionEffectProvider; -import org.spongepowered.common.util.SpongeMinecraftDayTime; - -import java.util.OptionalLong; public final class WorldTypeData { @@ -54,9 +53,9 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.WORLD_TYPE_EFFECT) .get(WorldTypeData::worldTypeEffect) .create(Keys.SCORCHING) - .get(DimensionType::ultraWarm) + .get(d -> d.attributes().applyModifier(EnvironmentAttributes.WATER_EVAPORATES, false)) .create(Keys.NATURAL_WORLD_TYPE) - .get(DimensionType::natural) + .get(d -> d.attributes().contains(EnvironmentAttributes.BED_RULE)) .create(Keys.COORDINATE_MULTIPLIER) .get(DimensionType::coordinateScale) .create(Keys.HAS_SKYLIGHT) @@ -64,17 +63,19 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.HAS_CEILING) .get(DimensionType::hasCeiling) .create(Keys.PIGLIN_SAFE) - .get(DimensionType::piglinSafe) + .get(d -> d.attributes().applyModifier(EnvironmentAttributes.NETHER_PORTAL_SPAWNS_PIGLINS, false)) .create(Keys.BEDS_USABLE) - .get(DimensionType::bedWorks) + .get(d -> d.attributes().applyModifier(EnvironmentAttributes.BED_RULE, BedRule.CAN_SLEEP_WHEN_DARK).explodes()) .create(Keys.RESPAWN_ANCHOR_USABLE) - .get(DimensionType::respawnAnchorWorks) + .get(d -> d.attributes().applyModifier(EnvironmentAttributes.RESPAWN_ANCHOR_WORKS, false)) .create(Keys.INFINIBURN) - .get(dimensionType -> (Tag) (Object) dimensionType.infiniburn()) + .get(dimensionType -> dimensionType.infiniburn() instanceof HolderSet.Named named + ? (Tag) (Object) named.key() + : null) .create(Keys.WORLD_FLOOR) .get(DimensionType::minY) .create(Keys.HAS_RAIDS) - .get(DimensionType::hasRaids) + .get(d -> d.attributes().applyModifier(EnvironmentAttributes.CAN_START_RAID, true)) .create(Keys.WORLD_HEIGHT) .get(DimensionType::height) .create(Keys.WORLD_LOGICAL_HEIGHT) @@ -86,8 +87,6 @@ public static void register(final DataProviderRegistrator registrator) { .asImmutable(DimensionTypeAccessor.class) .create(Keys.AMBIENT_LIGHTING) .get(DimensionTypeAccessor::accessor$ambientLight) - .create(Keys.FIXED_TIME) - .get(WorldTypeData::fixedTime) .asImmutable(DimensionTypeBridge.class) .create(Keys.CREATE_DRAGON_FIGHT) .get(DimensionTypeBridge::bridge$createDragonFight) @@ -97,24 +96,15 @@ public static void register(final DataProviderRegistrator registrator) { // @formatter:on private static Range lightRange(IntProvider provider) { - return Range.intRange(provider.getMinValue(), provider.getMaxValue()); + return Range.intRange(provider.minInclusive(), provider.maxInclusive()); } private static WorldTypeEffect worldTypeEffect(final DimensionType type) { - final var key = (ResourceKey) (Object) type.effectsLocation(); - @Nullable final WorldTypeEffect effect = DimensionEffectProvider.INSTANCE.get(key); + @Nullable final WorldTypeEffect effect = DimensionEffectProvider.INSTANCE.get(type.skybox()); if (effect == null) { - throw new IllegalStateException(String.format("The effect '%s' has not been registered!", key)); + throw new IllegalStateException(String.format("The effect '%s' has not been registered!", type.skybox())); } return effect; } - @Nullable - private static MinecraftDayTime fixedTime(final DimensionTypeAccessor accessor) { - final OptionalLong fixedTime = accessor.accessor$fixedTime(); - if (!fixedTime.isPresent()) { - return null; - } - return new SpongeMinecraftDayTime(fixedTime.getAsLong()); - } } diff --git a/src/main/java/org/spongepowered/common/data/provider/world/biome/BiomeData.java b/src/main/java/org/spongepowered/common/data/provider/world/biome/BiomeData.java index 063864fca71..8d5fa86f272 100644 --- a/src/main/java/org/spongepowered/common/data/provider/world/biome/BiomeData.java +++ b/src/main/java/org/spongepowered/common/data/provider/world/biome/BiomeData.java @@ -25,6 +25,9 @@ package org.spongepowered.common.data.provider.world.biome; import net.minecraft.core.Holder; +import net.minecraft.util.random.Weighted; +import net.minecraft.world.attribute.AmbientSounds; +import net.minecraft.world.attribute.EnvironmentAttributes; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.BiomeGenerationSettings; @@ -42,7 +45,6 @@ import org.spongepowered.api.world.biome.spawner.NaturalSpawnCost; import org.spongepowered.api.world.biome.spawner.NaturalSpawner; import org.spongepowered.api.world.generation.carver.Carver; -import org.spongepowered.api.world.generation.carver.CarvingStep; import org.spongepowered.api.world.generation.feature.DecorationStep; import org.spongepowered.api.world.generation.feature.PlacedFeature; import org.spongepowered.common.accessor.world.level.biome.BiomeAccessor; @@ -88,27 +90,42 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.NATURAL_SPAWNER_COST) .get(BiomeData::naturalSpawnerCost) .create(Keys.FOG_COLOR) - .get(h -> Color.ofRgb(h.getSpecialEffects().getFogColor())) + .get(h -> { + final var value = h.getAttributes().applyModifier(EnvironmentAttributes.FOG_COLOR, EnvironmentAttributes.FOG_COLOR.defaultValue()); + return Color.ofRgb(value); + }) .create(Keys.WATER_COLOR) - .get(h -> Color.ofRgb(h.getSpecialEffects().getWaterColor())) + .get(h -> Color.ofRgb(h.getSpecialEffects().waterColor())) .create(Keys.WATER_FOG_COLOR) - .get(h -> Color.ofRgb(h.getSpecialEffects().getWaterFogColor())) + .get(h -> { + final var value = h.getAttributes().applyModifier(EnvironmentAttributes.WATER_FOG_COLOR, EnvironmentAttributes.WATER_FOG_COLOR.defaultValue()); + return Color.ofRgb(value); + }) .create(Keys.SKY_COLOR) - .get(h -> Color.ofRgb(h.getSpecialEffects().getSkyColor())) + .get(h -> { + final var value = h.getAttributes().applyModifier(EnvironmentAttributes.SKY_COLOR, EnvironmentAttributes.SKY_COLOR.defaultValue()); + return Color.ofRgb(value); + }) .create(Keys.FOLIAGE_COLOR) - .get(h -> h.getSpecialEffects().getFoliageColorOverride().map(Color::ofRgb).orElse(null)) + .get(h -> h.getSpecialEffects().foliageColorOverride().map(Color::ofRgb).orElse(null)) .create(Keys.GRASS_COLOR) - .get(h -> h.getSpecialEffects().getGrassColorOverride().map(Color::ofRgb).orElse(null)) + .get(h -> h.getSpecialEffects().grassColorOverride().map(Color::ofRgb).orElse(null)) .create(Keys.GRASS_COLOR_MODIFIER) - .get(h -> (GrassColorModifier) (Object) h.getSpecialEffects().getGrassColorModifier()) + .get(h -> (GrassColorModifier) (Object) h.getSpecialEffects().grassColorModifier()) .create(Keys.BACKGROUND_MUSIC) - .get(h -> h.getSpecialEffects().getBackgroundMusic().map(SoundConfig.BackgroundMusic.class::cast).orElse(null)) + .get(h -> { + final var value = h.getAttributes().applyModifier(EnvironmentAttributes.BACKGROUND_MUSIC, EnvironmentAttributes.BACKGROUND_MUSIC.defaultValue()); + return (SoundConfig.BackgroundMusic) (Object) value.defaultMusic().orElse(null); + }) .create(Keys.AMBIENT_ADDITIONAL_SOUND) - .get(h -> h.getSpecialEffects().getAmbientAdditionsSettings().map(SoundConfig.Additional.class::cast).orElse(null)) + .get(h -> { + final var value = h.getAttributes().applyModifier(EnvironmentAttributes.AMBIENT_SOUNDS, EnvironmentAttributes.AMBIENT_SOUNDS.defaultValue()); + return (SoundConfig.Additional) (Object) value.additions().stream().findFirst().orElse(null); + }) .create(Keys.AMBIENT_MOOD) - .get(h -> h.getSpecialEffects().getAmbientMoodSettings().map(SoundConfig.Mood.class::cast).orElse(null)) + .get(h -> h.getAttributes().applyModifier(EnvironmentAttributes.AMBIENT_SOUNDS, AmbientSounds.EMPTY).mood().map(SoundConfig.Mood.class::cast).orElse(null)) .create(Keys.AMBIENT_PARTICLE) - .get(h -> h.getSpecialEffects().getAmbientParticleSettings().map(ParticleConfig.class::cast).orElse(null)) + .get(h -> h.getAttributes().applyModifier(EnvironmentAttributes.AMBIENT_PARTICLES,List.of()).stream().findFirst().map(ParticleConfig.class::cast).orElse(null)) .asImmutable(BiomeAccessor.class) .create(Keys.HUMIDITY) .get(h -> (double) h.accessor$climateSettings().downfall()) @@ -119,15 +136,9 @@ public static void register(final DataProviderRegistrator registrator) { } // @formatter:on - private static Map> carvers(final Biome biome) { + public static List carvers(final Biome biome) { final var settings = biome.getGenerationSettings(); - return Arrays.stream(GenerationStep.Carving.values()) - .collect(Collectors.toMap(step -> (CarvingStep) (Object) step, - step -> BiomeData.carverList(settings, step))); - } - - private static List carverList(final BiomeGenerationSettings settings, final GenerationStep.Carving step) { - final var carvers = settings.getCarvers(step); + final var carvers = settings.getCarvers(); return StreamSupport.stream(carvers.spliterator(), false) .map(carver -> (Carver) (Object) carver.value()) .collect(Collectors.toList()); @@ -158,13 +169,36 @@ private static Map> naturalSpawners(Biome b return map; } + public record WeightedSpanwer(Weighted data) implements NaturalSpawner { + + @Override + public EntityType type() { + return (EntityType) this.data.value().type(); + } + + @Override + public int min() { + return this.data.value().minCount(); + } + + @Override + public int max() { + return this.data.value().maxCount(); + } + + @Override + public int weight() { + return this.data.weight(); + } + } + private static Optional> naturalSpawner(Biome biome, MobCategory cat) { - final List unwrap = biome.getMobSettings().getMobs(cat).unwrap(); + final List> unwrap = biome.getMobSettings().getMobs(cat).unwrap(); if (unwrap.isEmpty()) { return Optional.empty(); } final List result = new ArrayList<>(); - unwrap.forEach(data -> result.add((NaturalSpawner) data)); + unwrap.forEach(data -> result.add(new WeightedSpanwer(data))); return Optional.of(result); } diff --git a/src/main/java/org/spongepowered/common/data/type/SpongeArtTypeBuilder.java b/src/main/java/org/spongepowered/common/data/type/SpongeArtTypeBuilder.java new file mode 100644 index 00000000000..fe9da7ba032 --- /dev/null +++ b/src/main/java/org/spongepowered/common/data/type/SpongeArtTypeBuilder.java @@ -0,0 +1,85 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.type; + +import net.minecraft.resources.Identifier; +import net.minecraft.world.entity.decoration.painting.PaintingVariant; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.data.type.ArtType; +import org.spongepowered.common.util.Preconditions; + +import java.util.Objects; +import java.util.Optional; + +public final class SpongeArtTypeBuilder implements ArtType.Builder { + + private int width; + private int height; + @Nullable private Identifier assetId; + + public SpongeArtTypeBuilder() { + this.reset(); + } + + @Override + public ArtType.Builder dimensions(final int width, final int height) { + this.width = width; + this.height = height; + return this; + } + + @Override + public ArtType.Builder asset(final ResourceKey assetId) { + this.assetId = (Identifier) (Object) assetId; + return this; + } + + @Override + public ArtType.Builder from(final ArtType value) { + if ((Object) value instanceof final PaintingVariant variant) { + this.width = variant.width(); + this.height = variant.height(); + this.assetId = variant.assetId(); + } + return this; + } + + @Override + public ArtType.Builder reset() { + this.width = 0; + this.height = 0; + this.assetId = null; + return this; + } + + @Override + public ArtType build() { + Preconditions.checkArgument(this.width >= 0, "width must set"); + Preconditions.checkArgument(this.height >= 0, "height must set"); + Objects.requireNonNull(this.assetId, "assetId"); + return (ArtType) (Object) new PaintingVariant(this.width, this.height, this.assetId, Optional.empty(), Optional.empty()); + } +} diff --git a/src/main/java/org/spongepowered/common/data/type/SpongeArtTypeTemplate.java b/src/main/java/org/spongepowered/common/data/type/SpongeArtTypeTemplate.java deleted file mode 100644 index b97360fe3d6..00000000000 --- a/src/main/java/org/spongepowered/common/data/type/SpongeArtTypeTemplate.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.data.type; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.RegistryOps; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.decoration.PaintingVariant; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.data.type.ArtType; -import org.spongepowered.api.data.type.ArtTypeTemplate; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; -import org.spongepowered.common.util.Preconditions; - -import java.io.IOException; -import java.util.Objects; -import java.util.function.Function; - -public record SpongeArtTypeTemplate(ResourceKey key, PaintingVariant representedType, DataPack pack) implements ArtTypeTemplate { - - @Override - public ArtType type() { - return (ArtType) (Object) this.representedType; - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeArtTypeTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized DamageType:\n" + serialized, e); - } - } - - public static JsonElement encode(final ArtTypeTemplate template, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return PaintingVariant.DIRECT_CODEC.encodeStart(ops, (PaintingVariant) (Object) template.type()).getOrThrow(); - } - - public static PaintingVariant decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return PaintingVariant.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static SpongeArtTypeTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final var parsed = SpongeArtTypeTemplate.decode(packEntry, registryAccess); - return new SpongeArtTypeTemplate(key, parsed, pack); - } - - public static final class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - private int width; - private int height; - private ResourceLocation assetId; - - @Override - public Builder dimensions(final int width, final int height) { - this.width = width; - this.height = height; - return this; - } - - @Override - public Builder asset(final ResourceKey assetId) { - this.assetId = (ResourceLocation) (Object) assetId; - return this; - } - - @Override - public Builder fromValue(final ArtType value) { - if ((Object) value instanceof PaintingVariant variant) { - this.width = variant.width(); - this.height = variant.height(); - this.assetId = variant.assetId(); - } - return this; - } - - @Override - public Builder fromDataPack(final DataView dataView) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(dataView)); - final var damageType = SpongeArtTypeTemplate.decode(json, SpongeCommon.server().registryAccess()); - this.fromValue((ArtType) (Object) damageType); - return this; - } - - @Override - public Function valueExtractor() { - return ArtTypeTemplate::type; - } - - @Override - protected ArtTypeTemplate build0() { - Preconditions.checkArgument(this.width >= 0, "width must set"); - Preconditions.checkArgument(this.height >= 0, "height must set"); - Objects.requireNonNull(this.assetId, "assetId"); - return new SpongeArtTypeTemplate(this.key, new PaintingVariant(this.width, this.height, this.assetId), this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/datapack/DataPackDecoder.java b/src/main/java/org/spongepowered/common/datapack/DataPackDecoder.java deleted file mode 100644 index 85a9b41d0ec..00000000000 --- a/src/main/java/org/spongepowered/common/datapack/DataPackDecoder.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.datapack; - -import net.minecraft.core.RegistryAccess; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPackEntry; - -public interface DataPackDecoder> { - - T decode(DataPack pack, ResourceKey key, E from, RegistryAccess registryAccess); - -} diff --git a/src/main/java/org/spongepowered/common/datapack/DataPackSerializer.java b/src/main/java/org/spongepowered/common/datapack/DataPackSerializer.java deleted file mode 100644 index fa934269483..00000000000 --- a/src/main/java/org/spongepowered/common/datapack/DataPackSerializer.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.datapack; - -import com.google.gson.JsonObject; -import net.minecraft.SharedConstants; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.PackType; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.datapack.DataPackEntry; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; - -public abstract class DataPackSerializer> { - - protected DataPackEncoder encoder; - protected DataPackDecoder decoder; - - public DataPackSerializer(final DataPackEncoder encoder, final DataPackDecoder decoder) { - this.encoder = encoder; - this.decoder = decoder; - } - - public boolean serialize(final SpongeDataPack pack, final Path packDir, final List packEntries) throws IOException { - if (packEntries.isEmpty()) { - return false; - } - - // Write our objects - for (final T packEntry : packEntries) { - this.serializeObject(pack, packDir, packEntry); - this.serializeAdditional(pack, packDir, packEntry); - } - return true; - } - - public Path packEntryFile(final SpongeDataPackType packType, final ResourceKey key, final Path packDir) { - final ResourceLocation loc = this.location(packType, key); - return packDir.resolve("data") - .resolve(loc.getNamespace()) - .resolve(loc.getPath()); - } - - public ResourceLocation location(final SpongeDataPackType packType, final ResourceKey key) { - return ResourceLocation.fromNamespaceAndPath(key.namespace(), packType.dir() + "/" + key.value() + this.fileEnding()); - } - - public abstract String fileEnding(); - - public abstract void serializeObject(final SpongeDataPack pack, final Path packDir, final T packEntry) throws IOException; - - protected abstract void serializeAdditional(final SpongeDataPack type, Path packDir, T entry) throws IOException; - - public abstract T deserialize(final SpongeDataPack pack, final Path file, final ResourceKey key) throws IOException; - - public abstract T deserialize(final SpongeDataPack pack, final InputStream is, final ResourceKey key) throws IOException; - - - - public static void writePackMetadata(final SpongeDataPack pack, final Path packDir, final boolean replace) throws IOException { - // Write our pack metadata - final Path packMeta = packDir.resolve("pack.mcmeta"); - if (replace || !Files.exists(packMeta)) { - final JsonObject packDataRoot = new JsonObject(); - final JsonObject packData = new JsonObject(); - packDataRoot.add("pack", packData); - packData.addProperty("pack_format", SharedConstants.getCurrentVersion().getPackVersion(PackType.SERVER_DATA)); - packData.addProperty("description", pack.description()); - - JsonDataPackSerializer.writeFile(packMeta, packDataRoot); - } - } - -} diff --git a/src/main/java/org/spongepowered/common/datapack/JsonDataPackSerializer.java b/src/main/java/org/spongepowered/common/datapack/JsonDataPackSerializer.java deleted file mode 100644 index 7ec99ec73dc..00000000000 --- a/src/main/java/org/spongepowered/common/datapack/JsonDataPackSerializer.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.datapack; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.datapack.DataPackEntry; -import org.spongepowered.common.SpongeCommon; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.file.Files; -import java.nio.file.Path; - -public class JsonDataPackSerializer> extends DataPackSerializer { - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - - public JsonDataPackSerializer(final DataPackEncoder encoder, final DataPackDecoder decoder) { - super(encoder, decoder); - } - - @Override - public String fileEnding() { - return ".json"; - } - - @Override - public void serializeObject(final SpongeDataPack pack, final Path packDir, final T packEntry) throws IOException { - final JsonElement serialized = this.encoder.encode(packEntry, SpongeCommon.server().registryAccess()); - final Path file = this.packEntryFile(pack.type(), packEntry.key(), packDir); - final JsonElement finalJson = this.transformSerialized(file, packEntry, serialized); - Files.createDirectories(file.getParent()); - JsonDataPackSerializer.writeFile(file, finalJson); - } - - protected JsonElement transformSerialized(final Path file, final T entry, final JsonElement serialized) throws IOException { - return serialized; - } - - @Override - protected void serializeAdditional(final SpongeDataPack type, Path packDir, T entry) throws IOException { - } - - @Override - public T deserialize(final SpongeDataPack pack, final Path file, final ResourceKey key) throws IOException { - try (final InputStream stream = Files.newInputStream(file)) { - return this.deserialize(pack, stream, key); - } - } - - @Override - public T deserialize(final SpongeDataPack pack, final InputStream is, final ResourceKey key) throws IOException { - try (final InputStreamReader reader = new InputStreamReader(is)) { - final JsonElement element = JsonParser.parseReader(reader); - - if (this.decoder != null) { - // TODO this is actually blocking - return this.decoder.decode(pack, key, element, SpongeCommon.server().registryAccess()); - } - } - return null; - } - - public static void writeFile(final Path file, final JsonElement object) throws IOException { - try (BufferedWriter writer = Files.newBufferedWriter(file)) { - JsonDataPackSerializer.GSON.toJson(object, writer); - } - } - -} diff --git a/src/main/java/org/spongepowered/common/datapack/NbtDataPackSerializer.java b/src/main/java/org/spongepowered/common/datapack/NbtDataPackSerializer.java deleted file mode 100644 index 8ded4c9327f..00000000000 --- a/src/main/java/org/spongepowered/common/datapack/NbtDataPackSerializer.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.datapack; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtAccounter; -import net.minecraft.nbt.NbtIo; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.datapack.DataPackEntry; -import org.spongepowered.common.SpongeCommon; - -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; - -public class NbtDataPackSerializer> extends DataPackSerializer { - - public NbtDataPackSerializer(final DataPackEncoder encoder, final DataPackDecoder decoder) { - super(encoder, decoder); - } - - @Override - public String fileEnding() { - return ".nbt"; - } - - @Override - public void serializeObject(final SpongeDataPack pack, final Path packDir, final T packEntry) throws IOException { - final CompoundTag serialized = this.encoder.encode(packEntry, SpongeCommon.server().registryAccess()); - final Path file = this.packEntryFile(pack.type(), packEntry.key(), packDir); - Files.createDirectories(file.getParent()); - NbtDataPackSerializer.writeFile(file, serialized); - } - - @Override - protected void serializeAdditional(final SpongeDataPack type, Path packDir, T entry) throws IOException { - } - - @Override - public T deserialize(final SpongeDataPack pack, final Path file, final ResourceKey key) throws IOException { - try (final InputStream stream = Files.newInputStream(file)) { - return this.deserialize(pack, stream, key); - } - } - - @Override - public T deserialize(final SpongeDataPack pack, final InputStream is, final ResourceKey key) throws IOException { - try (final DataInputStream dis = new DataInputStream(is)) { - final CompoundTag element = NbtIo.readCompressed(dis, NbtAccounter.unlimitedHeap()); - if (this.decoder != null) { - // TODO this is actually blocking - return this.decoder.decode(pack, key, element, SpongeCommon.server().registryAccess()); - } - } - return null; - } - - public static void writeFile(final Path file, final CompoundTag object) throws IOException { - Files.deleteIfExists(file); - NbtIo.writeCompressed(object, file); - } - -} diff --git a/src/main/java/org/spongepowered/common/datapack/SpongeDataPack.java b/src/main/java/org/spongepowered/common/datapack/SpongeDataPack.java deleted file mode 100644 index a2000ec1e12..00000000000 --- a/src/main/java/org/spongepowered/common/datapack/SpongeDataPack.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.datapack; - -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPackEntry; - -public record SpongeDataPack>(String name, String description, SpongeDataPackType type) implements DataPack { - -} diff --git a/src/main/java/org/spongepowered/common/datapack/SpongeDataPackManager.java b/src/main/java/org/spongepowered/common/datapack/SpongeDataPackManager.java index 59d5dbde859..635e24e6479 100644 --- a/src/main/java/org/spongepowered/common/datapack/SpongeDataPackManager.java +++ b/src/main/java/org/spongepowered/common/datapack/SpongeDataPackManager.java @@ -24,45 +24,14 @@ */ package org.spongepowered.common.datapack; -import com.google.gson.JsonElement; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.packs.PackResources; -import net.minecraft.server.packs.PackType; -import net.minecraft.server.packs.metadata.pack.PackMetadataSection; import net.minecraft.server.packs.repository.PackRepository; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.server.packs.resources.ResourceManager; -import org.apache.commons.io.FilenameUtils; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPackEntry; -import org.spongepowered.api.datapack.DataPackType; -import org.spongepowered.api.datapack.DataPackTypes; -import org.spongepowered.api.event.Cause; -import org.spongepowered.api.event.EventContext; import org.spongepowered.api.world.server.DataPackManager; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.event.lifecycle.RegisterDataPackValueEventImpl; -import org.spongepowered.common.item.recipe.ingredient.IngredientResultUtil; -import org.spongepowered.common.item.recipe.ingredient.SpongeIngredient; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; -import java.util.stream.Stream; // TODO make IO non-blocking // TODO not all data-pack entries are on disk - this is currently not handled @@ -77,17 +46,6 @@ public SpongeDataPackManager(final MinecraftServer server, final Path packsDir) this.packsDir = packsDir; } - // TODO IntegratedServer call? - public void init() { - this.ignoreNext = false; - final List reloadablePacks = this.registerPacks(); - if (!reloadablePacks.isEmpty()) { - SpongeCommon.logger().info("Reloading for plugin data packs... " + reloadablePacks.size()); - this.ignoreNext = true; - this.server.reloadResources(this.discoverNewPacks()); - } - } - // see ReloadCommand#discoverNewPacks private List discoverNewPacks() { final PackRepository packRepo = this.server.getPackRepository(); @@ -107,239 +65,4 @@ public CompletableFuture reload() { this.ignoreNext = false; return this.server.reloadResources(this.discoverNewPacks()); } - - public List registerPacks() { - final List reloadablePacks = new ArrayList<>(); - - // Ignore reload immediately after first call - if (this.ignoreNext) { - this.ignoreNext = false; - return reloadablePacks; - } - - SpongeIngredient.clearCache(); - IngredientResultUtil.clearCache(); - - this.registerAndSerializePack(DataPackTypes.ADVANCEMENT, reloadablePacks); - this.registerAndSerializePack(DataPackTypes.RECIPE, reloadablePacks); - this.registerAndSerializePack(DataPackTypes.BLOCK_TAG, reloadablePacks); - this.registerAndSerializePack(DataPackTypes.DAMAGE_TYPE, reloadablePacks); - this.registerAndSerializePack(DataPackTypes.DAMAGE_TYPE_TAG, reloadablePacks); - return reloadablePacks; - } - - private > List callRegisterDataPackValueEvent(final SpongeDataPackType type) { - final RegisterDataPackValueEventImpl event = new RegisterDataPackValueEventImpl<>(Cause.of(EventContext.empty(), SpongeCommon.game()), SpongeCommon.game(), type); - SpongeCommon.post(event); - return event.serializables(); - } - - @SuppressWarnings("unchecked") - private > void registerAndSerializePack(final DataPackType type, final Collection reloadablePacks) { - final List packEntries = this.callRegisterDataPackValueEvent((SpongeDataPackType) type); - if (packEntries.isEmpty()) { - return; - } - this.serializePack(reloadablePacks, packEntries); - } - - private > void serializePack(final Collection reloadablePacks, final List packEntries) { - final Map, List> packEntryMap = packEntries.stream().collect(Collectors.groupingBy(DataPackEntry::pack)); - for (final Map.Entry, List> entry : packEntryMap.entrySet()) { - final SpongeDataPack implPack = (SpongeDataPack) entry.getKey(); - final String fullPackName = "file/" + implPack.name(); - try { - final boolean success = implPack.type().packSerializer().serialize(implPack, this.packDir(implPack), entry.getValue()); - DataPackSerializer.writePackMetadata(implPack, this.packDir(implPack), false); - if (success && implPack.type().reloadable()) { - if (!reloadablePacks.contains(fullPackName)) { - reloadablePacks.add(fullPackName); - } - } else { - reloadablePacks.remove(fullPackName); - } - } catch (final IOException e) { - reloadablePacks.remove(fullPackName); - SpongeCommon.logger().error(e); - } - } - } - - @Override - public > CompletableFuture save(final T entry) { - if (entry.pack().name().equals("Default")) { // cannot save into vanilla builtin pack - return CompletableFuture.completedFuture(false); - } - // TODO this is actually blocking - this.serializePack(new ArrayList<>(), List.of(entry)); - return CompletableFuture.completedFuture(((SpongeDataPackType)entry.pack().type()).reloadable()); - } - - @Override - public > CompletableFuture> load(final DataPack pack, final ResourceKey key) { - Objects.requireNonNull(key, "key"); - final SpongeDataPack implPack = (SpongeDataPack) pack; - final ResourceManager manager = this.server.getResourceManager(); - final DataPackSerializer packSerializer = implPack.type().packSerializer(); - final Optional resource = manager.getResource(packSerializer.location(implPack.type(), key)); - if (resource.isPresent()) { // loaded resource (includes vanilla builtin) - try (InputStream inputStream = resource.get().open()) { - final T deserialized = packSerializer.deserialize(implPack, inputStream, key); - return CompletableFuture.completedFuture(Optional.ofNullable(deserialized)); - } catch (final IOException ex) { - return CompletableFuture.failedFuture(ex); - } - } - - // Next attempt to find file on disk - final Path file = this.packFile(implPack, key); - if (Files.exists(file)) { - try { - // TODO this is actually blocking - final T deserialized = packSerializer.deserialize(implPack, file, key); - return CompletableFuture.completedFuture(Optional.ofNullable(deserialized)); - } catch (final IOException ex) { - return CompletableFuture.failedFuture(ex); - } - } - return CompletableFuture.completedFuture(Optional.empty()); - } - - @Override - public boolean exists(final DataPack pack, final ResourceKey key) { - final SpongeDataPack packImpl = (SpongeDataPack) pack; - final Path file = this.packFile(packImpl, Objects.requireNonNull(key, "key")); - return Files.exists(file); - } - - @Override - public boolean delete(final DataPack pack, final ResourceKey key) throws IOException { - final SpongeDataPack packImpl = (SpongeDataPack) pack; - final Path file = this.packFile(packImpl, Objects.requireNonNull(key, "key")); - return Files.deleteIfExists(file); - } - - @Override - public void copy(final DataPack pack, final ResourceKey from, final ResourceKey to) throws IOException { - this.copy(pack, from, pack, to); - } - - @Override - public void copy(final DataPack fromPack, final ResourceKey from, final DataPack toPack, final ResourceKey to) throws IOException { - final SpongeDataPack packImpl = (SpongeDataPack) fromPack; - final SpongeDataPack packImplTo = (SpongeDataPack) toPack; - final Path fileFrom = this.packFile(packImpl, Objects.requireNonNull(from, "from")); - final Path fileto = this.packFile(packImplTo, Objects.requireNonNull(to, "to")); - Files.createDirectories(fileto.getParent()); - Files.copy(fileFrom, fileto, StandardCopyOption.REPLACE_EXISTING); - } - - @Override - public void move(final DataPack pack, final ResourceKey from, final ResourceKey to) throws IOException { - this.move(pack, from, pack, to); - } - - - @Override - public void move(final DataPack fromPack, final ResourceKey from, final DataPack toPack, final ResourceKey to) throws IOException { - final SpongeDataPack packImpl = (SpongeDataPack) fromPack; - final SpongeDataPack packImplTo = (SpongeDataPack) toPack; - final Path fileFrom = this.packFile(packImpl, Objects.requireNonNull(from, "from")); - final Path fileto = this.packFile(packImplTo, Objects.requireNonNull(to, "to")); - Files.createDirectories(fileto.getParent()); - Files.copy(fileFrom, fileto, StandardCopyOption.REPLACE_EXISTING); - } - - - @Override - public List list(final DataPack pack) { - final List packEntries = new ArrayList<>(); - final Path packDir = this.packDir(pack); - if (!Files.isDirectory(packDir)) { - return packEntries; - } - try (final Stream namespaces = Files.walk(packDir.resolve("data"), 1)) { - namespaces.filter(Files::isDirectory).forEach(namespaceDir -> { - final Path typeDir = namespaceDir.resolve(((SpongeDataPack) pack).type().dir()); - if (Files.isDirectory(typeDir)) { - try (final Stream pluginTemplates = Files.walk(typeDir, 1)) { - pluginTemplates.filter(file -> file.toString().endsWith(".json")) - .map(file -> SpongeDataPackManager.keyFor(namespaceDir, file)) - .forEach(packEntries::add); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - }); - } catch (final IOException e) { - throw new RuntimeException(e); - } - return packEntries; - } - - @Override - public > Map, Collection> find(final DataPackType packType) { - final ResourceManager manager = this.server.getResourceManager(); - final Map> resources = new HashMap<>(); - final Map descriptions = new HashMap<>(); - final SpongeDataPackType typeImpl = (SpongeDataPackType) packType; - manager.listPacks().forEach(pack -> { - final String packName = pack.packId(); - descriptions.computeIfAbsent(packName, k -> this.packDescription(pack)); - for (final String namespace : pack.getNamespaces(PackType.SERVER_DATA)) { - final Collection locs = new HashSet<>(); - pack.listResources(PackType.SERVER_DATA, namespace, typeImpl.dir(), (loc, stream) -> locs.add(loc)); - if (!locs.isEmpty()) { - resources.computeIfAbsent(packName, k -> new ArrayList<>()).addAll(locs); - } - } - }); - - final Map, Collection> map = new HashMap<>(); - for (final Map.Entry> entry : resources.entrySet()) { - map.put(packType.pack(entry.getKey(), descriptions.get(entry.getKey())), entry.getValue().stream().map(loc -> { - final String path = loc.getPath(); - final String value = path.substring(typeImpl.dir().length() + 1, path.length() - typeImpl.packSerializer().fileEnding().length()); - return ResourceKey.of(loc.getNamespace(), value); - }).toList()); - } - return map; - } - - @Override - public > Optional> findPack(final DataPackType packType, final ResourceKey key) { - final SpongeDataPackType implType = (SpongeDataPackType) packType; - final ResourceLocation compareLoc = implType.packSerializer().location(implType, key); - for (final Resource resource : this.server.getResourceManager().listResources(((SpongeDataPackType) packType).dir(), loc -> loc.equals(compareLoc)).values()) { - String name = resource.sourcePackId(); - if (name.startsWith("file/")) { - name = name.substring(5); - } - return Optional.of(packType.pack(name, "N/A")); - } - return Optional.empty(); - } - - private String packDescription(final PackResources pack) { - try { - return pack.getMetadataSection(PackMetadataSection.TYPE).description().getString(); - } catch (final IOException e) { - return "N/A"; - } - } - - private static ResourceKey keyFor(final Path namespaceDir, final Path file) { - final String namespace = namespaceDir.getFileName().toString(); - final String value = FilenameUtils.removeExtension(file.getFileName().toString()); - return ResourceKey.of(namespace, value); - } - - private > Path packFile(final SpongeDataPack pack, final ResourceKey key) { - return pack.type().packSerializer().packEntryFile(pack.type(), key, this.packDir(pack)); - } - - private Path packDir(final DataPack pack) { - return this.packsDir.resolve(pack.name()); - } - } diff --git a/src/main/java/org/spongepowered/common/datapack/SpongeDataPackType.java b/src/main/java/org/spongepowered/common/datapack/SpongeDataPackType.java deleted file mode 100644 index 1b1b9aabe2e..00000000000 --- a/src/main/java/org/spongepowered/common/datapack/SpongeDataPackType.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.datapack; - -import com.google.gson.JsonElement; -import io.leangen.geantyref.TypeToken; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.advancement.AdvancementTemplate; -import org.spongepowered.api.adventure.ChatTypeTemplate; -import org.spongepowered.api.data.type.ArtTypeTemplate; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPackEntry; -import org.spongepowered.api.datapack.DataPackType; -import org.spongepowered.api.event.cause.entity.damage.DamageTypeTemplate; -import org.spongepowered.api.item.recipe.RecipeRegistration; -import org.spongepowered.api.registry.RegistryType; -import org.spongepowered.api.tag.TagTemplate; -import org.spongepowered.api.tag.Taggable; -import org.spongepowered.api.world.WorldTypeTemplate; -import org.spongepowered.api.world.biome.BiomeTemplate; -import org.spongepowered.api.world.generation.carver.CarverTemplate; -import org.spongepowered.api.world.generation.config.noise.DensityFunctionTemplate; -import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfigTemplate; -import org.spongepowered.api.world.generation.config.noise.NoiseTemplate; -import org.spongepowered.api.world.generation.feature.FeatureTemplate; -import org.spongepowered.api.world.generation.feature.PlacedFeatureTemplate; -import org.spongepowered.api.world.generation.structure.SchematicTemplate; -import org.spongepowered.api.world.generation.structure.StructureSetTemplate; -import org.spongepowered.api.world.generation.structure.StructureTemplate; -import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPoolTemplate; -import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorListTemplate; -import org.spongepowered.api.world.server.WorldTemplate; -import org.spongepowered.common.advancement.SpongeAdvancementTemplate; -import org.spongepowered.common.adventure.SpongeChatTypeTemplate; -import org.spongepowered.common.data.type.SpongeArtTypeTemplate; -import org.spongepowered.common.datapack.recipe.RecipeDataPackSerializer; -import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTypeTemplate; -import org.spongepowered.common.item.recipe.SpongeRecipeRegistration; -import org.spongepowered.common.tag.SpongeTagTemplate; -import org.spongepowered.common.world.biome.SpongeBiomeTemplate; -import org.spongepowered.common.world.generation.carver.SpongeCarverTemplate; -import org.spongepowered.common.world.generation.config.noise.SpongeDensityFunctionTemplate; -import org.spongepowered.common.world.generation.config.noise.SpongeNoiseGeneratorConfigTemplate; -import org.spongepowered.common.world.generation.config.noise.SpongeNoiseTemplate; -import org.spongepowered.common.world.generation.feature.SpongeFeatureTemplate; -import org.spongepowered.common.world.generation.feature.SpongePlacedFeatureTemplate; -import org.spongepowered.common.world.generation.structure.SpongeSchematicTemplate; -import org.spongepowered.common.world.generation.structure.SpongeStructureSetTemplate; -import org.spongepowered.common.world.generation.structure.SpongeStructureTemplate; -import org.spongepowered.common.world.generation.structure.jigsaw.SpongeJigsawPoolTemplate; -import org.spongepowered.common.world.generation.structure.jigsaw.SpongeProcessorListTemplate; -import org.spongepowered.common.world.server.SpongeWorldTemplate; -import org.spongepowered.common.world.server.SpongeWorldTypeTemplate; - -public record SpongeDataPackType>(String dir, boolean reloadable, TypeToken entryType, DataPackSerializer packSerializer) implements DataPackType { - - public static > SpongeDataPackType basic(final Class clazz, final String dir, final DataPackEncoder encoder, final DataPackDecoder decoder, final boolean reloadable) { - final TypeToken tt = TypeToken.get(clazz); - return new SpongeDataPackType<>(dir, reloadable, tt, new JsonDataPackSerializer<>(encoder, decoder)); - } - - public static > SpongeDataPackType custom(final Class clazz, final String dir, final DataPackSerializer serializer, final boolean reloadable) { - final TypeToken tt = TypeToken.get(clazz); - return new SpongeDataPackType<>(dir, reloadable, tt, serializer); - } - - public static > SpongeDataPackType custom(final TypeToken typeToken, final String dir, final DataPackSerializer serializer, final boolean reloadable) { - return new SpongeDataPackType<>(dir, reloadable, typeToken, serializer); - } - - public static final class FactoryImpl implements DataPackType.Factory { - - private final SpongeDataPackType advancement = SpongeDataPackType.basic(AdvancementTemplate.class, - "advancement", SpongeAdvancementTemplate::encode, null, // TODO decoder - true); - - private final SpongeDataPackType recipe = SpongeDataPackType.custom(RecipeRegistration.class, - "recipe", new RecipeDataPackSerializer(SpongeRecipeRegistration::encode, null), // TODO decoder - true); - - private final SpongeDataPackType worldType = SpongeDataPackType.basic(WorldTypeTemplate.class, - "dimension_type", SpongeWorldTypeTemplate::encode, SpongeWorldTypeTemplate::decode, - false); - - private final SpongeDataPackType world = SpongeDataPackType.basic(WorldTemplate.class, - "dimension", SpongeWorldTemplate::encode, SpongeWorldTemplate::decode, - false); - - private final SpongeDataPackType biome = SpongeDataPackType.basic(BiomeTemplate.class, - "worldgen/biome", SpongeBiomeTemplate::encode, SpongeBiomeTemplate::decode, - false); - - private final SpongeDataPackType carver = SpongeDataPackType.basic(CarverTemplate.class, - "worldgen/configured_carver", SpongeCarverTemplate::encode, SpongeCarverTemplate::decode, - false); - - private final SpongeDataPackType feature = SpongeDataPackType.basic(FeatureTemplate.class, - "worldgen/configured_feature", SpongeFeatureTemplate::encode, SpongeFeatureTemplate::decode, - false); - - private final SpongeDataPackType placedFeature = SpongeDataPackType.basic(PlacedFeatureTemplate.class, - "worldgen/placed_feature", SpongePlacedFeatureTemplate::encode, SpongePlacedFeatureTemplate::decode, - false); - - private final SpongeDataPackType noiseGeneratorConfig = SpongeDataPackType.basic(NoiseGeneratorConfigTemplate.class, - "worldgen/noise_settings", SpongeNoiseGeneratorConfigTemplate::encode, SpongeNoiseGeneratorConfigTemplate::decode, - false); - - private final SpongeDataPackType noise = SpongeDataPackType.basic(NoiseTemplate.class, - "worldgen/noise", SpongeNoiseTemplate::encode, SpongeNoiseTemplate::decode, - false); - - private final SpongeDataPackType densityFunction = SpongeDataPackType.basic(DensityFunctionTemplate.class, - "worldgen/density_function", SpongeDensityFunctionTemplate::encode, SpongeDensityFunctionTemplate::decode, - false); - - private final SpongeDataPackType structure = SpongeDataPackType.basic(StructureTemplate.class, - "worldgen/structure", SpongeStructureTemplate::encode, SpongeStructureTemplate::decode, - false); - - private final SpongeDataPackType schematic = SpongeDataPackType.custom(SchematicTemplate.class, - "structure", new NbtDataPackSerializer<>(SpongeSchematicTemplate::encode, SpongeSchematicTemplate::decode), - false); - - private final SpongeDataPackType processorList = SpongeDataPackType.basic(ProcessorListTemplate.class, - "worldgen/processor_list", SpongeProcessorListTemplate::encode, SpongeProcessorListTemplate::decode, - false); - - private final SpongeDataPackType structureSet = SpongeDataPackType.basic(StructureSetTemplate.class, - "worldgen/structure_set", SpongeStructureSetTemplate::encode, SpongeStructureSetTemplate::decode, - false); - - private final SpongeDataPackType jigsawPool = SpongeDataPackType.basic(JigsawPoolTemplate.class, - "worldgen/template_pool", SpongeJigsawPoolTemplate::encode, SpongeJigsawPoolTemplate::decode, - false); - - // TODO should be reloadable https://bugs.mojang.com/browse/MC-251318 - private final SpongeDataPackType chatType = SpongeDataPackType.basic(ChatTypeTemplate.class, - "chat_type", SpongeChatTypeTemplate::encode, SpongeChatTypeTemplate::decode, - false); - - private final SpongeDataPackType damageType = SpongeDataPackType.basic(DamageTypeTemplate.class, - "damage_type", SpongeDamageTypeTemplate::encode, SpongeDamageTypeTemplate::decode, - false); - - private final SpongeDataPackType artType = SpongeDataPackType.basic(ArtTypeTemplate.class, - "damage_type", SpongeArtTypeTemplate::encode, SpongeArtTypeTemplate::decode, - false); - - @Override - public DataPackType recipe() { - return this.recipe; - } - - @Override - public DataPackType advancement() { - return this.advancement; - } - - @Override - public DataPackType worldType() { - return this.worldType; - } - - @Override - public DataPackType world() { - return world; - } - - @Override - public DataPackType biome() { - return this.biome; - } - - @Override - public DataPackType carver() { - return this.carver; - } - - @Override - public DataPackType feature() { - return this.feature; - } - - @Override - public DataPackType placedFeature() { - return this.placedFeature; - } - - @Override - public DataPackType noiseGeneratorConfig() { - return this.noiseGeneratorConfig; - } - - @Override - public DataPackType noise() { - return this.noise; - } - - @Override - public DataPackType densityFunction() { - return this.densityFunction; - } - - @Override - public DataPackType structure() { - return this.structure; - } - - @Override - public DataPackType schematic() { - return this.schematic; - } - - @Override - public DataPackType processorList() { - return this.processorList; - } - - @Override - public DataPackType structureSet() { - return this.structureSet; - } - - @Override - public DataPackType jigsawPool() { - return this.jigsawPool; - } - - @Override - public DataPackType chatType() { - return this.chatType; - } - - @Override - public DataPackType damageType() { - return this.damageType; - } - - @Override - public > DataPackType> tag(RegistryType registry) { - final String tagDir = Registries.tagsDirPath(ResourceKey.createRegistryKey((ResourceLocation) (Object) registry.location())); - return SpongeDataPackType.custom(new TypeToken<>() {}, tagDir, - new TagDataPackSerializer<>(SpongeTagTemplate::encode, null), // TODO decoder - true); - } - - @Override - public DataPackType artType() { - return this.artType; - } - } - - public DataPack pack(final String name, final String description) { - return new SpongeDataPack<>(name, description, this); - } -} diff --git a/src/main/java/org/spongepowered/common/datapack/TagDataPackSerializer.java b/src/main/java/org/spongepowered/common/datapack/TagDataPackSerializer.java deleted file mode 100644 index 83402f25b70..00000000000 --- a/src/main/java/org/spongepowered/common/datapack/TagDataPackSerializer.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.datapack; - -import com.google.gson.JsonElement; -import org.spongepowered.api.tag.TagTemplate; -import org.spongepowered.api.tag.Taggable; - -import java.io.IOException; -import java.nio.file.Path; - -public final class TagDataPackSerializer> extends JsonDataPackSerializer> { - - public TagDataPackSerializer(final DataPackEncoder> encoder, final DataPackDecoder> decoder) { - super(encoder, decoder); - } - - @Override - protected JsonElement transformSerialized(final Path file, final TagTemplate entry, final JsonElement serialized) throws IOException{ - final JsonElement toWrite = serialized; - /* TODO support merging - if (Files.exists(file) && !((SpongeTagTemplate) entry).replace()) { - final JsonObject jsonObject; - try (BufferedReader bufferedReader = Files.newBufferedReader(file)) { - final JsonElement jsonElement = JsonParser.parseReader(bufferedReader); - jsonObject = jsonElement.getAsJsonObject(); - } - - toWrite = Tag.Builder.tag().addFromJson(jsonObject, filename).addFromJson(toWrite, filename).serializeToJson(); - // TagFile.CODEC.encodeStart(JsonOps.INSTANCE, new TagFile(builder.build(), this.replace)).getOrThrow(false, e -> {}).getAsJsonObject(); - } - //*/ - return toWrite; - } - -} diff --git a/src/main/java/org/spongepowered/common/datapack/recipe/RecipeDataPackSerializer.java b/src/main/java/org/spongepowered/common/datapack/recipe/RecipeDataPackSerializer.java deleted file mode 100644 index 27588abbdb6..00000000000 --- a/src/main/java/org/spongepowered/common/datapack/recipe/RecipeDataPackSerializer.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.datapack.recipe; - -import com.google.gson.JsonElement; -import com.mojang.serialization.JsonOps; -import net.minecraft.advancements.Advancement; -import org.spongepowered.api.item.recipe.RecipeRegistration; -import org.spongepowered.common.datapack.DataPackDecoder; -import org.spongepowered.common.datapack.DataPackEncoder; -import org.spongepowered.common.datapack.JsonDataPackSerializer; -import org.spongepowered.common.datapack.SpongeDataPack; -import org.spongepowered.common.item.recipe.SpongeRecipeRegistration; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -public final class RecipeDataPackSerializer extends JsonDataPackSerializer { - - public RecipeDataPackSerializer(final DataPackEncoder encoder, - final DataPackDecoder decoder) { - super(encoder, decoder); - } - - @Override - protected void serializeAdditional( - final SpongeDataPack type, final Path packDir, - final RecipeRegistration entry) throws IOException { - if (entry instanceof SpongeRecipeRegistration spongeReg) { - final var serialized = Advancement.CODEC.encodeStart(JsonOps.INSTANCE, spongeReg.advancement().value()); - - final Path file = packDir.resolve("data") - .resolve(entry.key().namespace()) - .resolve("advancement") - .resolve(entry.key().value() + ".json"); - Files.createDirectories(file.getParent()); - - JsonDataPackSerializer.writeFile(file, serialized.result().get()); - } - } -} diff --git a/src/main/java/org/spongepowered/common/effect/SpongeForwardingViewer.java b/src/main/java/org/spongepowered/common/effect/SpongeForwardingViewer.java index 74b7197b3c6..d964a819cfb 100644 --- a/src/main/java/org/spongepowered/common/effect/SpongeForwardingViewer.java +++ b/src/main/java/org/spongepowered/common/effect/SpongeForwardingViewer.java @@ -32,6 +32,7 @@ import net.minecraft.network.protocol.game.ClientGamePacketListener; import org.spongepowered.api.effect.ForwardingViewer; import org.spongepowered.api.effect.sound.music.MusicDisc; +import org.spongepowered.api.item.inventory.ItemStackLike; import org.spongepowered.common.bridge.effect.ViewerBridge; public interface SpongeForwardingViewer extends SpongeViewer, ForwardingViewer, ViewerBridge { @@ -50,6 +51,11 @@ public interface SpongeForwardingViewer extends SpongeViewer, ForwardingViewer, // Viewer + @Override + default void playTotemOfUndyingEffect(final ItemStackLike stack) { + this.audiences().forEach(viewer -> viewer.playTotemOfUndyingEffect(stack)); + } + @Override default void playMusicDisc(final int x, final int y, final int z, final MusicDisc musicDisc) { this.audiences().forEach(viewer -> viewer.playMusicDisc(x, y, z, musicDisc)); diff --git a/src/main/java/org/spongepowered/common/effect/particle/SpongeParticleHelper.java b/src/main/java/org/spongepowered/common/effect/particle/SpongeParticleHelper.java index 3e582e1edab..04aff666ee7 100644 --- a/src/main/java/org/spongepowered/common/effect/particle/SpongeParticleHelper.java +++ b/src/main/java/org/spongepowered/common/effect/particle/SpongeParticleHelper.java @@ -33,12 +33,14 @@ import net.minecraft.core.particles.SculkChargeParticleOptions; import net.minecraft.core.particles.ShriekParticleOption; import net.minecraft.core.particles.SimpleParticleType; +import net.minecraft.core.particles.TrailParticleOption; import net.minecraft.core.particles.VibrationParticleOption; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundBundlePacket; import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; -import net.minecraft.util.FastColor; +import net.minecraft.util.ARGB; +import net.minecraft.world.phys.Vec3; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.effect.particle.ParticleEffect; @@ -49,8 +51,10 @@ import org.spongepowered.api.util.Ticks; import org.spongepowered.api.world.PositionSource; import org.spongepowered.common.item.util.ItemStackUtil; +import org.spongepowered.common.util.VecHelper; import org.spongepowered.math.vector.Vector3d; import org.spongepowered.math.vector.Vector3f; +import org.spongepowered.math.vector.Vector3i; import java.util.ArrayList; import java.util.Collections; @@ -95,8 +99,11 @@ private static CachedParticlePacket getNamedPacket(final ParticleEffect effect, // The only way we can see what options are supported for a particular named particle // is to compare the internal type's deserializer to some static deserializer fields. // If only mojang had some type akin to our ParticleEffect... - if (internalType == ParticleTypes.BLOCK || internalType == ParticleTypes.BLOCK_MARKER - || internalType == ParticleTypes.FALLING_DUST || internalType == ParticleTypes.DUST_PILLAR) { + if (internalType == ParticleTypes.BLOCK || + internalType == ParticleTypes.BLOCK_MARKER || + internalType == ParticleTypes.FALLING_DUST || + internalType == ParticleTypes.DUST_PILLAR || + internalType == ParticleTypes.BLOCK_CRUMBLE) { //This particle type supports a block state option. final BlockState state = effect.optionOrDefault(ParticleOptions.BLOCK_STATE).get(); final BlockParticleOption particleData = new BlockParticleOption( @@ -108,34 +115,22 @@ private static CachedParticlePacket getNamedPacket(final ParticleEffect effect, final Color toColor = effect.optionOrDefault(ParticleOptions.TO_COLOR).get(); final double scale = effect.optionOrDefault(ParticleOptions.SCALE).get(); final DustColorTransitionOptions particleData = new DustColorTransitionOptions( - new org.joml.Vector3f( - (float) color.red() / 255, - (float) color.green() / 255, - (float) color.blue() / 255 - ), - new org.joml.Vector3f( - (float) toColor.red() / 255, - (float) toColor.green() / 255, - (float) toColor.blue() / 255 - ), + ARGB.color(color.red(), color.green(), color.blue()), + ARGB.color(toColor.red(), toColor.green(), toColor.blue()), (float) scale); return new NamedCachedPacket(particleData, offset, quantity, velocity); } else if (internalType == ParticleTypes.DUST) { //This particle type supports a color option. final Color color = effect.optionOrDefault(ParticleOptions.COLOR).get(); final double scale = effect.optionOrDefault(ParticleOptions.SCALE).get(); - final DustParticleOptions particleData = new DustParticleOptions(new org.joml.Vector3f( - (float) color.red() / 255, - (float) color.green() / 255, - (float) color.blue() / 255), - (float) scale); + final DustParticleOptions particleData = new DustParticleOptions(ARGB.color(color.red(), color.green(), color.blue()), (float) scale); return new NamedCachedPacket(particleData, offset, quantity, velocity); } else if (internalType == ParticleTypes.ITEM) { //This particle type supports an item option. final ItemStackSnapshot snapshot = effect.optionOrDefault(ParticleOptions.ITEM_STACK_SNAPSHOT).get(); final ItemParticleOption particleData = new ItemParticleOption( (net.minecraft.core.particles.ParticleType) internalType, - ItemStackUtil.fromSnapshotToNative(snapshot)); + ItemStackUtil.toTemplate(snapshot)); return new NamedCachedPacket(particleData, offset, quantity, velocity); } else if (internalType == ParticleTypes.SCULK_CHARGE) { final double roll = effect.optionOrDefault(ParticleOptions.ROLL).get(); @@ -158,7 +153,14 @@ private static CachedParticlePacket getNamedPacket(final ParticleEffect effect, final double opacity = effect.optionOrDefault(ParticleOptions.OPACITY).get(); final ColorParticleOption particleData = ColorParticleOption.create( (net.minecraft.core.particles.ParticleType) internalType, - FastColor.ARGB32.color(FastColor.as8BitChannel((float) opacity), color.red(), color.green(), color.blue())); + ARGB.color(ARGB.as8BitChannel((float) opacity), color.red(), color.green(), color.blue())); + return new NamedCachedPacket(particleData, offset, quantity, velocity); + } else if (internalType == ParticleTypes.TRAIL) { + // This particle type color, destination and duration + final var dest = effect.optionOrDefault(ParticleOptions.TARGET).orElse(Vector3d.ZERO); + final var duration = effect.optionOrDefault(ParticleOptions.TRAVEL_TIME).orElse(Ticks.of(20)); + final var color = effect.optionOrDefault(ParticleOptions.COLOR).map(c -> ARGB.color(c.red(), c.green(), c.blue())).orElse(16545810); + final var particleData = new TrailParticleOption(VecHelper.toVanillaVector3d(dest), color, (int) duration.ticks()); return new NamedCachedPacket(particleData, offset, quantity, velocity); } @@ -208,6 +210,12 @@ public static ParticleEffect spongeParticleOptions(final net.minecraft.core.part Vector3f.from(colorOptions.getRed(), colorOptions.getGreen(), colorOptions.getBlue()).mul(255)), ParticleOptions.OPACITY.get(), colorOptions.getAlpha() )); + } else if (effect instanceof TrailParticleOption(Vec3 target, int color, int duration)) { + return new SpongeParticleEffect((ParticleType) type, Map.of( + ParticleOptions.COLOR.get(), Color.of(Vector3i.from(ARGB.red(color), ARGB.green(color), ARGB.blue(color))), + ParticleOptions.TRAVEL_TIME.get(), Ticks.of(duration), + ParticleOptions.TARGET.get(), VecHelper.toVector3d(target) + )); } return new SpongeParticleEffect((ParticleType) type, Collections.emptyMap()); @@ -255,6 +263,7 @@ public void process(final double x, final double y, final double z, final List

totemOfUndying(final Player player, final ItemStackLike stack) { + final ItemStack item = ItemStackUtil.fromLikeToNativeCopy(stack); + // Client will play item animation only if it has DEATH_PROTECTION component, + // otherwise it will use totem of undying + if (!item.has(DataComponents.DEATH_PROTECTION)) { + item.set(DataComponents.DEATH_PROTECTION, new DeathProtection(List.of())); + } + + final net.minecraft.world.entity.player.Player mcPlayer = (net.minecraft.world.entity.player.Player) player; + final Inventory inventory = mcPlayer.getInventory(); + final int totemSlot = inventory.getSelectedSlot(); + final net.minecraft.world.item.ItemStack oldItem = inventory.getSelectedItem(); + + final ClientboundEntityEventPacket effectPacket = new ClientboundEntityEventPacket(mcPlayer, (byte) 35); + if (ItemStack.matches(oldItem, item)) { + return effectPacket; + } + + final List> packets = new ArrayList<>(); + + // First we need to send a fake totem ItemStack to the player's hand + packets.add(new ClientboundSetPlayerInventoryPacket(totemSlot, item)); + + // Next we tell the client to play the totem of undying effect + packets.add(effectPacket); + + // Now we can remove the fake totem + packets.add(new ClientboundSetPlayerInventoryPacket(totemSlot, oldItem)); + + return new ClientboundBundlePacket(packets); + } + public static ClientboundBundlePacket spawnParticles(final ParticleEffect particleEffect, final double x, final double y, final double z) { return SpongeParticleHelper.createPacket(particleEffect, x, y, z); } @@ -102,14 +143,14 @@ public static ClientboundSoundPacket playSound(final Sound sound, final RandomSo public static ClientboundStopSoundPacket stopSound(final SoundStop stop) { Objects.requireNonNull(stop, "stop"); - final @Nullable ResourceLocation sound = SpongeAdventure.asVanillaNullable(stop.sound()); + final @Nullable Identifier sound = SpongeAdventure.asVanillaLocation(stop.sound()); final @Nullable SoundSource source = SpongeAdventure.asVanillaNullable(stop.source()); return new ClientboundStopSoundPacket(sound, source); } public static ClientboundLevelEventPacket playMusicDisc(final int x, final int y, final int z, final MusicDisc musicDisc, final RegistryAccess registryAccess) { Objects.requireNonNull(musicDisc, "musicDisc"); - final int songId = registryAccess.registryOrThrow(Registries.JUKEBOX_SONG).getId((JukeboxSong) (Object) musicDisc); + final int songId = registryAccess.lookupOrThrow(Registries.JUKEBOX_SONG).getId((JukeboxSong) (Object) musicDisc); return new ClientboundLevelEventPacket(1010, new BlockPos(x, y, z), songId, false); } @@ -178,7 +219,7 @@ public static ClientboundClearTitlesPacket clearTitles(final boolean resetTimes) } private static Holder resolveEvent(final @NonNull Sound sound) { - final ResourceLocation soundKey = SpongeAdventure.asVanilla(Objects.requireNonNull(sound, "sound").name()); + final Identifier soundKey = SpongeAdventure.asVanilla(Objects.requireNonNull(sound, "sound").name()); final var soundEventRegistry = SpongeCommon.vanillaRegistry(Registries.SOUND_EVENT); final SoundEvent event = soundEventRegistry.getOptional(soundKey) .orElseGet(() -> SoundEvent.createVariableRangeEvent(soundKey)); diff --git a/src/main/java/org/spongepowered/common/entity/EntityDataUtil.java b/src/main/java/org/spongepowered/common/entity/EntityDataUtil.java index a6bc365e602..568ef2e55e3 100644 --- a/src/main/java/org/spongepowered/common/entity/EntityDataUtil.java +++ b/src/main/java/org/spongepowered/common/entity/EntityDataUtil.java @@ -92,17 +92,15 @@ private static DataView upgrade(final DataView container, final ImmutableList(NbtOps.INSTANCE, stackData), dataVersion, - SharedConstants.getCurrentVersion().getDataVersion().getVersion() + SharedConstants.getCurrentVersion().dataVersion().version() ); return updatedContainer.set(dataQuery, NBTTranslator.INSTANCE.translate((CompoundTag) fixedData.getValue())); } public static EntityType entityType(final CompoundTag entityData) throws InvalidDataException { - if (!entityData.contains(Constants.Entity.ENTITY_TYPE_ID, Constants.NBT.TAG_STRING)) { - throw new InvalidDataException("Missing entity type id in entity data"); - } - final String entityTypeId = entityData.getString(Constants.Entity.ENTITY_TYPE_ID); + final String entityTypeId = entityData.getString(Constants.Entity.ENTITY_TYPE_ID) + .orElseThrow(() -> new InvalidDataException("Missing entity type id in entity data")); return Sponge.game().registry(RegistryTypes.ENTITY_TYPE) .findValue(ResourceKey.resolve(entityTypeId)) .orElseThrow(() -> new InvalidDataException("Could not deserialize an EntityType: " + entityTypeId)); @@ -128,7 +126,7 @@ public DataView update(final DataView content) { updated.remove(Constants.Entity.TYPE); updated.set(Queries.CONTENT_VERSION, this.outputVersion()); - updated.set(Constants.Entity.V2.DATA_VERSION, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + updated.set(Constants.Entity.V2.DATA_VERSION, SharedConstants.getCurrentVersion().dataVersion().version()); updated.set(Constants.Entity.V2.DATA, content.getView(Constants.Sponge.UNSAFE_NBT) .map(DataView::copy) .orElseGet(() -> DataContainer.createNew(DataView.SafetyMode.NO_DATA_CLONED)) @@ -158,7 +156,7 @@ public DataView update(final DataView content) { updated.remove(Constants.Sponge.EntityArchetype.V1.ENTITY_TYPE); updated.set(Queries.CONTENT_VERSION, this.outputVersion()); - updated.set(Constants.Sponge.EntityArchetype.V2.DATA_VERSION, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + updated.set(Constants.Sponge.EntityArchetype.V2.DATA_VERSION, SharedConstants.getCurrentVersion().dataVersion().version()); updated.set(Constants.Sponge.EntityArchetype.V2.DATA, content.getView(Constants.Sponge.EntityArchetype.V1.ENTITY_DATA) .map(DataView::copy) diff --git a/src/main/java/org/spongepowered/common/entity/EntityUtil.java b/src/main/java/org/spongepowered/common/entity/EntityUtil.java index 7bca0b0c175..e5fb1382c26 100644 --- a/src/main/java/org/spongepowered/common/entity/EntityUtil.java +++ b/src/main/java/org/spongepowered/common/entity/EntityUtil.java @@ -26,10 +26,9 @@ import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; -import net.minecraft.util.random.SimpleWeightedRandomList; -import net.minecraft.util.random.WeightedEntry; +import net.minecraft.util.random.WeightedList; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.item.ItemEntity; @@ -184,9 +183,9 @@ public static boolean isUntargetable(final Entity from, final Entity target) { public static EntityArchetype toArchetype(final SpawnData logic) { final var tag = logic.entityToSpawn(); - final var resourceLocation = tag.getString(Constants.Entity.ENTITY_TYPE_ID); + final var identifier = tag.getString(Constants.Entity.ENTITY_TYPE_ID); final var entityTypeRegistry = SpongeCommon.vanillaRegistry(Registries.ENTITY_TYPE); - final var type = entityTypeRegistry.getOptional(ResourceLocation.parse(resourceLocation)) + final var type = identifier.flatMap(location -> entityTypeRegistry.getOptional(Identifier.parse(location))) .map(org.spongepowered.api.entity.EntityType.class::cast) .orElse(EntityTypes.PIG.get()); @@ -207,13 +206,16 @@ public static SpawnData toSpawnData(final EntityArchetype value) { return new SpawnData(tag, Optional.empty(), Optional.empty()); } - public static WeightedTable toWeightedArchetypes(final SimpleWeightedRandomList spawnData) { + public static WeightedTable toWeightedArchetypes(final WeightedList spawnData) { final WeightedTable possibleEntities = new WeightedTable<>(); - for (final WeightedEntry.Wrapper weightedEntity : spawnData.unwrap()) { - final CompoundTag nbt = weightedEntity.data().entityToSpawn(); - final String resourceLocation = nbt.getString(Constants.Entity.ENTITY_TYPE_ID); - final var mcType = SpongeCommon.vanillaRegistry(Registries.ENTITY_TYPE).getOptional(ResourceLocation.parse(resourceLocation)); + for (final var weightedEntity : spawnData.unwrap()) { + final CompoundTag nbt = weightedEntity.value().entityToSpawn(); + final Optional identifier = nbt.getString(Constants.Entity.ENTITY_TYPE_ID); + if (identifier.isEmpty()) { + continue; + } + final var mcType = SpongeCommon.vanillaRegistry(Registries.ENTITY_TYPE).getOptional(Identifier.parse(identifier.get())); final var type = mcType.map(org.spongepowered.api.entity.EntityType.class::cast).orElse(EntityTypes.PIG.get()); final EntityArchetype archetype = SpongeEntityArchetypeBuilder.pooled() @@ -221,13 +223,13 @@ public static WeightedTable toWeightedArchetypes(final SimpleWe .entityData(NBTTranslator.INSTANCE.translateFrom(nbt)) .build(); - possibleEntities.add(new WeightedSerializableObject<>(archetype, weightedEntity.getWeight().asInt())); + possibleEntities.add(new WeightedSerializableObject<>(archetype, weightedEntity.weight())); } return possibleEntities; } - public static SimpleWeightedRandomList toSpawnPotentials(final WeightedTable table) { - final SimpleWeightedRandomList.Builder builder = SimpleWeightedRandomList.builder(); + public static WeightedList toSpawnPotentials(final WeightedTable table) { + final WeightedList.Builder builder = WeightedList.builder(); for (final var entry : table) { if (entry instanceof final WeightedObject weightedObj) { final var tag = NBTTranslator.INSTANCE.translate(weightedObj.get().entityData()); diff --git a/src/main/java/org/spongepowered/common/entity/SpongeEntityArchetype.java b/src/main/java/org/spongepowered/common/entity/SpongeEntityArchetype.java index 626d61ab955..02591acaec5 100644 --- a/src/main/java/org/spongepowered/common/entity/SpongeEntityArchetype.java +++ b/src/main/java/org/spongepowered/common/entity/SpongeEntityArchetype.java @@ -29,9 +29,11 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.DoubleTag; import net.minecraft.nbt.ListTag; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.EntitySpawnRequest; import net.minecraft.world.entity.Mob; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -53,6 +55,7 @@ import org.spongepowered.common.hooks.PlatformHooks; import org.spongepowered.common.util.Constants; import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3f; import java.util.Objects; import java.util.Optional; @@ -95,14 +98,14 @@ public Optional getPosition() { if (this.position != null) { return Optional.of(this.position); } - if (!this.compound.contains(Constants.Entity.ENTITY_POSITION, Constants.NBT.TAG_LIST)) { + if (!this.compound.contains(Constants.Entity.ENTITY_POSITION)) { return Optional.empty(); } try { - final ListTag pos = this.compound.getList(Constants.Entity.ENTITY_POSITION, Constants.NBT.TAG_DOUBLE); - final double x = pos.getDouble(0); - final double y = pos.getDouble(1); - final double z = pos.getDouble(2); + final ListTag pos = this.compound.getListOrEmpty(Constants.Entity.ENTITY_POSITION); + final double x = pos.getDoubleOr(0, 0); + final double y = pos.getDoubleOr(1, 0); + final double z = pos.getDoubleOr(2, 0); this.position = new Vector3d(x, y, z); return Optional.of(this.position); } catch (final Exception e) { @@ -133,7 +136,7 @@ public Optional apply(final ServerLocation final org.spongepowered.api.world.server.ServerWorld spongeWorld = location.world(); final ServerLevel level = (ServerLevel) spongeWorld; - final ResourceLocation key = net.minecraft.world.entity.EntityType.getKey((net.minecraft.world.entity.EntityType) this.type); + final Identifier key = net.minecraft.world.entity.EntityType.getKey((net.minecraft.world.entity.EntityType) this.type); if (key == null) { return Optional.empty(); } @@ -147,8 +150,8 @@ public Optional apply(final ServerLocation compound.put(Constants.Entity.ENTITY_POSITION, pos); compound.remove(Constants.Entity.ENTITY_UUID); - final @Nullable Entity entity = net.minecraft.world.entity.EntityType.loadEntityRecursive(compound, level, e -> { - e.moveTo(location.x(), location.y(), location.z()); + final @Nullable Entity entity = net.minecraft.world.entity.EntityType.loadEntityRecursive(compound, level, new EntitySpawnRequest(EntitySpawnReason.LOAD, false), e -> { + e.snapTo(location.x(), location.y(), location.z()); if (e instanceof Mob mobentity) { mobentity.yHeadRot = mobentity.getYRot(); mobentity.yBodyRot = mobentity.getXRot(); @@ -183,9 +186,9 @@ public EntitySnapshot toSnapshot(final ServerLocation location) { } private Vector3d getRotation() { - final ListTag tag = this.compound.getList("Rotation", 5); - final float rotationYaw = tag.getFloat(0); - final float rotationPitch = tag.getFloat(1); + final var rotation = this.compound.read("Rotation", Constants.Entity.ROTATIONS_CODEC).orElse(Vector3f.ZERO); + final float rotationYaw = rotation.x(); + final float rotationPitch = rotation.z(); return new Vector3d(rotationPitch, rotationYaw, 0); } @@ -198,7 +201,7 @@ public int contentVersion() { public DataContainer toContainer() { return DataContainer.createNew(DataView.SafetyMode.NO_DATA_CLONED) .set(Queries.CONTENT_VERSION, this.contentVersion()) - .set(Constants.Sponge.EntityArchetype.V2.DATA_VERSION, SharedConstants.getCurrentVersion().getDataVersion().getVersion()) + .set(Constants.Sponge.EntityArchetype.V2.DATA_VERSION, SharedConstants.getCurrentVersion().dataVersion().version()) .set(Constants.Sponge.EntityArchetype.V2.DATA, this.entityData() .set(Constants.Entity.ENTITY_TYPE_ID, net.minecraft.world.entity.EntityType.getKey((net.minecraft.world.entity.EntityType) this.type))); } diff --git a/src/main/java/org/spongepowered/common/entity/SpongeEntityArchetypeBuilder.java b/src/main/java/org/spongepowered/common/entity/SpongeEntityArchetypeBuilder.java index e8c89077ced..7dd3c9697fa 100644 --- a/src/main/java/org/spongepowered/common/entity/SpongeEntityArchetypeBuilder.java +++ b/src/main/java/org/spongepowered/common/entity/SpongeEntityArchetypeBuilder.java @@ -25,6 +25,8 @@ package org.spongepowered.common.entity; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.level.storage.TagValueOutput; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -128,13 +130,14 @@ public EntityArchetype.Builder from(final Entity entity) { throw new IllegalArgumentException("Attempting to archetype a non-serializable entity: " + entity); } this.entityType = entityType; - final CompoundTag compound = new CompoundTag(); net.minecraft.world.entity.Entity mcEntity = (net.minecraft.world.entity.Entity) entity; + final var output = TagValueOutput.createWithContext(ProblemReporter.DISCARDING, mcEntity.level().registryAccess()); if (entityType == SpongeEntityTypes.HUMAN) { - mcEntity.saveWithoutId(compound); + mcEntity.saveWithoutId(output); } else { - mcEntity.saveAsPassenger(compound); + mcEntity.saveAsPassenger(output); } + final CompoundTag compound = output.buildResult(); this.position = new Vector3d(mcEntity.getX(), mcEntity.getY(), mcEntity.getZ()); SpongeEntityArchetypeBuilder.stripCompound(compound); this.position = entity.position(); diff --git a/src/main/java/org/spongepowered/common/entity/SpongeEntitySnapshot.java b/src/main/java/org/spongepowered/common/entity/SpongeEntitySnapshot.java index 25ac1760a7d..0bbacfe5e4b 100644 --- a/src/main/java/org/spongepowered/common/entity/SpongeEntitySnapshot.java +++ b/src/main/java/org/spongepowered/common/entity/SpongeEntitySnapshot.java @@ -26,6 +26,8 @@ import net.minecraft.SharedConstants; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.level.storage.TagValueInput; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; @@ -148,7 +150,7 @@ public DataContainer toContainer() { .set(Queries.POSITION_Y, this.scale.y()) .set(Queries.POSITION_Z, this.scale.z()) .container() - .set(Constants.Entity.V2.DATA_VERSION, SharedConstants.getCurrentVersion().getDataVersion().getVersion()) + .set(Constants.Entity.V2.DATA_VERSION, SharedConstants.getCurrentVersion().dataVersion().version()) .set(Constants.Entity.V2.DATA, data); if (this.uniqueId != null) { @@ -231,7 +233,8 @@ public Optional restore() { if (newEntity != null) { final net.minecraft.world.entity.Entity nmsEntity = (net.minecraft.world.entity.Entity) newEntity; if (this.compound != null) { - nmsEntity.load(this.compound); + final var input = TagValueInput.create(ProblemReporter.DISCARDING, nmsEntity.level().registryAccess(), this.compound); + nmsEntity.load(input); } final boolean spawnResult = world.get().spawnEntity((Entity) nmsEntity); diff --git a/src/main/java/org/spongepowered/common/entity/SpongeEntitySnapshotBuilder.java b/src/main/java/org/spongepowered/common/entity/SpongeEntitySnapshotBuilder.java index 05914d49e6a..1a9a3cbb4e4 100644 --- a/src/main/java/org/spongepowered/common/entity/SpongeEntitySnapshotBuilder.java +++ b/src/main/java/org/spongepowered/common/entity/SpongeEntitySnapshotBuilder.java @@ -25,6 +25,8 @@ package org.spongepowered.common.entity; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.level.storage.TagValueOutput; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.data.DataManipulator; @@ -116,8 +118,10 @@ public SpongeEntitySnapshotBuilder from(final Entity entity) { this.entityType = entity.type(); this.uniqueId = entity.uniqueId(); this.manipulator = ((SpongeDataHolderBridge) entity).bridge$getManipulator().copy(); - this.compound = new CompoundTag(); - ((net.minecraft.world.entity.Entity) entity).saveWithoutId(this.compound); + final var mcEntity = (net.minecraft.world.entity.Entity) entity; + final var output = TagValueOutput.createWithContext(ProblemReporter.DISCARDING, mcEntity.level().registryAccess()); + mcEntity.saveWithoutId(output); + this.compound = output.buildResult(); return this; } @@ -164,8 +168,9 @@ public SpongeEntitySnapshotBuilder from(final net.minecraft.world.entity.Entity this.rotation = transform.rotation(); this.scale = transform.scale(); this.manipulator = DataManipulator.mutableOf((Entity) minecraftEntity); - this.compound = new CompoundTag(); - minecraftEntity.saveWithoutId(this.compound); + final var output = TagValueOutput.createWithContext(ProblemReporter.DISCARDING, minecraftEntity.level().registryAccess()); + minecraftEntity.saveWithoutId(output); + this.compound = output.buildResult(); return this; } diff --git a/src/main/java/org/spongepowered/common/entity/SpongeEntityTypes.java b/src/main/java/org/spongepowered/common/entity/SpongeEntityTypes.java index fd16c2c94a6..170008b6ac8 100644 --- a/src/main/java/org/spongepowered/common/entity/SpongeEntityTypes.java +++ b/src/main/java/org/spongepowered/common/entity/SpongeEntityTypes.java @@ -25,6 +25,9 @@ package org.spongepowered.common.entity; import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; import org.spongepowered.common.entity.living.human.HumanEntity; @@ -37,7 +40,7 @@ public class SpongeEntityTypes { .sized(0.6F, 1.8F) .clientTrackingRange(Constants.Entity.Player.TRACKING_RANGE) .updateInterval(2) - .build("sponge:human"); + .build(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.fromNamespaceAndPath("sponge", "human"))); public static void register(Registry> registry) { Registry.register(registry, HumanEntity.KEY, SpongeEntityTypes.HUMAN); diff --git a/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/SpongeAvoidLivingGoalBuilder.java b/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/SpongeAvoidLivingGoalBuilder.java index d71213c7d9f..a075bb4af60 100644 --- a/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/SpongeAvoidLivingGoalBuilder.java +++ b/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/SpongeAvoidLivingGoalBuilder.java @@ -29,6 +29,7 @@ import org.spongepowered.api.entity.ai.goal.builtin.creature.AvoidLivingGoal; import org.spongepowered.api.entity.living.Living; import org.spongepowered.api.entity.living.PathfinderAgent; +import org.spongepowered.api.world.server.ServerWorld; import java.util.Objects; import java.util.function.Predicate; @@ -69,7 +70,7 @@ public AvoidLivingGoal.Builder farRangeSpeed(double speed) { @Override public AvoidLivingGoal.Builder from(AvoidLivingGoal value) { - return this.targetSelector(value.targetSelector()) + return this.targetSelector((e) -> value.targetSelector().test(e, (ServerWorld) e.world())) .searchDistance(value.searchDistance()) .closeRangeSpeed(value.closeRangeSpeed()) .farRangeSpeed(value.farRangeSpeed()); diff --git a/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/horse/SpongeRunAroundLikeCrazyAIBuilder.java b/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/horse/SpongeRunAroundLikeCrazyAIBuilder.java index c4e1bcd75af..a024c9fd3fa 100644 --- a/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/horse/SpongeRunAroundLikeCrazyAIBuilder.java +++ b/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/horse/SpongeRunAroundLikeCrazyAIBuilder.java @@ -60,6 +60,6 @@ public RunAroundLikeCrazyGoal.Builder reset() { @Override public RunAroundLikeCrazyGoal build(HorseLike owner) { Objects.requireNonNull(owner); - return (RunAroundLikeCrazyGoal) new net.minecraft.world.entity.ai.goal.RunAroundLikeCrazyGoal((net.minecraft.world.entity.animal.horse.Horse) owner, this.speed); + return (RunAroundLikeCrazyGoal) new net.minecraft.world.entity.ai.goal.RunAroundLikeCrazyGoal((net.minecraft.world.entity.animal.equine.Horse) owner, this.speed); } } diff --git a/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/target/SpongeFindNearestAttackableTargetGoalBuilder.java b/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/target/SpongeFindNearestAttackableTargetGoalBuilder.java index a85069f64e2..435b72001e8 100644 --- a/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/target/SpongeFindNearestAttackableTargetGoalBuilder.java +++ b/src/main/java/org/spongepowered/common/entity/ai/goal/builtin/creature/target/SpongeFindNearestAttackableTargetGoalBuilder.java @@ -28,6 +28,7 @@ import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.PathfinderMob; import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; +import net.minecraft.world.entity.ai.targeting.TargetingConditions; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.entity.ai.goal.builtin.creature.target.FindNearestAttackableTargetGoal; import org.spongepowered.api.entity.living.Living; @@ -40,7 +41,7 @@ public final class SpongeFindNearestAttackableTargetGoalBuilder extends SpongeTargetGoalBuilder implements FindNearestAttackableTargetGoal.Builder { - private static final Predicate ALWAYS_TRUE = e -> true; + private static final TargetingConditions.Selector ALWAYS_TRUE = (e, l) -> true; private Class targetClass; private int chance; @@ -94,6 +95,6 @@ public FindNearestAttackableTargetGoal build(PathfinderAgent owner) { Objects.requireNonNull(this.targetClass); return (FindNearestAttackableTargetGoal) new NearestAttackableTargetGoal((PathfinderMob) owner, this.targetClass, this.chance, - this.checkSight, this.checkOnlyNearby, this.predicate == null ? SpongeFindNearestAttackableTargetGoalBuilder.ALWAYS_TRUE : this.predicate); + this.checkSight, this.checkOnlyNearby, this.predicate == null ? SpongeFindNearestAttackableTargetGoalBuilder.ALWAYS_TRUE : (e, l) -> ((Predicate) this.predicate).test(l)); } } diff --git a/src/main/java/org/spongepowered/common/entity/attribute/SpongeAttributeModifierBuilder.java b/src/main/java/org/spongepowered/common/entity/attribute/SpongeAttributeModifierBuilder.java index 5b29661f430..d5950911bb1 100644 --- a/src/main/java/org/spongepowered/common/entity/attribute/SpongeAttributeModifierBuilder.java +++ b/src/main/java/org/spongepowered/common/entity/attribute/SpongeAttributeModifierBuilder.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.entity.attribute; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.entity.attribute.AttributeModifier; import org.spongepowered.api.entity.attribute.AttributeOperation; @@ -35,14 +35,14 @@ public final class SpongeAttributeModifierBuilder implements AttributeModifier.B // Use a random id private AttributeOperation operation; private double amount; - private ResourceLocation key; + private Identifier key; public SpongeAttributeModifierBuilder() { } @Override public AttributeModifier.Builder key(final ResourceKey key) { - this.key = (ResourceLocation) (Object) key; + this.key = (Identifier) (Object) key; return this; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/TreeNodePositionMixin.java b/src/main/java/org/spongepowered/common/entity/attribute/SpongeItemAttributeFactory.java similarity index 50% rename from src/mixins/java/org/spongepowered/common/mixin/core/advancements/TreeNodePositionMixin.java rename to src/main/java/org/spongepowered/common/entity/attribute/SpongeItemAttributeFactory.java index 0e2157c6685..cafaadd17c5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/TreeNodePositionMixin.java +++ b/src/main/java/org/spongepowered/common/entity/attribute/SpongeItemAttributeFactory.java @@ -22,28 +22,31 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.advancements; +package org.spongepowered.common.entity.attribute; -import net.minecraft.advancements.AdvancementNode; -import net.minecraft.advancements.TreeNodePosition; -import org.spongepowered.api.advancement.AdvancementTree; -import org.spongepowered.api.event.Cause; -import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.advancement.AdvancementTreeEvent; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.EquipmentSlotGroup; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.item.component.ItemAttributeModifiers; +import org.spongepowered.api.entity.attribute.AttributeModifier; +import org.spongepowered.api.entity.attribute.ItemAttribute; +import org.spongepowered.api.entity.attribute.type.AttributeType; +import org.spongepowered.api.item.inventory.equipment.EquipmentCondition; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.event.tracking.PhaseTracker; -@Mixin(TreeNodePosition.class) -public abstract class TreeNodePositionMixin { +import java.util.Objects; - @Inject(method = "run", at = @At("RETURN")) - private static void impl$onLayout(AdvancementNode node, CallbackInfo ci) { - final Cause cause = PhaseTracker.getInstance().currentCause(); - final AdvancementTreeEvent.GenerateLayout event = SpongeEventFactory.createAdvancementTreeEventGenerateLayout(cause, (AdvancementTree) node); - SpongeCommon.post(event); +public final class SpongeItemAttributeFactory implements ItemAttribute.Factory { + + @Override + public ItemAttribute of(final AttributeType type, final AttributeModifier modifier, final EquipmentCondition condition) { + Objects.requireNonNull(type, "type"); + Objects.requireNonNull(modifier, "modifier"); + Objects.requireNonNull(condition, "condition"); + return (ItemAttribute) (Object) new ItemAttributeModifiers.Entry( + SpongeCommon.vanillaRegistry(Registries.ATTRIBUTE).wrapAsHolder((Attribute) type), + (net.minecraft.world.entity.ai.attributes.AttributeModifier) (Object) modifier, + (EquipmentSlotGroup) (Object) condition + ); } } diff --git a/src/main/java/org/spongepowered/common/entity/living/human/HumanEntity.java b/src/main/java/org/spongepowered/common/entity/living/human/HumanEntity.java index 20148bdec69..c83f97e5953 100644 --- a/src/main/java/org/spongepowered/common/entity/living/human/HumanEntity.java +++ b/src/main/java/org/spongepowered/common/entity/living/human/HumanEntity.java @@ -24,13 +24,15 @@ */ package org.spongepowered.common.entity.living.human; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; import com.mojang.authlib.yggdrasil.ProfileResult; import com.mojang.serialization.DataResult; import net.kyori.adventure.text.Component; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.HoverEvent; @@ -41,11 +43,12 @@ import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerEntity; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.NameAndId; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.util.Mth; @@ -53,6 +56,7 @@ import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EntityTypes; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.PathfinderMob; @@ -63,13 +67,15 @@ import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; import net.minecraft.world.entity.monster.RangedAttackMob; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.projectile.Arrow; +import net.minecraft.world.entity.projectile.arrow.Arrow; import net.minecraft.world.item.ArrowItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.component.ResolvableProfile; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.phys.Vec3; import net.minecraft.world.scores.PlayerTeam; import org.checkerframework.checker.nullness.qual.Nullable; @@ -80,9 +86,11 @@ import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.accessor.network.protocol.game.ClientboundAddEntityPacketAccessor; import org.spongepowered.common.accessor.network.protocol.game.ClientboundPlayerInfoUpdatePacketAccessor; +import org.spongepowered.common.accessor.world.entity.AvatarAccessor; import org.spongepowered.common.accessor.world.entity.LivingEntityAccessor; import org.spongepowered.common.accessor.world.entity.player.PlayerAccessor; import org.spongepowered.common.config.SpongeGameConfigs; +import org.spongepowered.common.data.datasync.NoOpSynchronizer; import org.spongepowered.common.launch.Launch; import org.spongepowered.common.profile.SpongeProfileProperty; import org.spongepowered.common.util.Constants; @@ -97,11 +105,13 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.OptionalInt; import java.util.UUID; +import java.util.function.Consumer; import java.util.stream.Stream; public final class HumanEntity extends PathfinderMob implements TeamMember, RangedAttackMob { - public static final ResourceKey> KEY = ResourceKey.create(Registries.ENTITY_TYPE, ResourceLocation.fromNamespaceAndPath("sponge", "human")); + public static final ResourceKey> KEY = ResourceKey.create(Registries.ENTITY_TYPE, Identifier.fromNamespaceAndPath("sponge", "human")); public static AttributeSupplier createAttributes() { return Mob.createMobAttributes() @@ -113,16 +123,16 @@ public static AttributeSupplier createAttributes() { } // A queue of packets waiting to send to players tracking this human - private final Map>>> playerPacketMap = new HashMap<>(); + private final Map>>> playerPacketMap = new HashMap<>(); private ResolvableProfile fakeProfile; private boolean aiDisabled = false, leftHanded = false; public HumanEntity(final EntityType type, final Level world) { super(type, world); - this.fakeProfile = new ResolvableProfile(new GameProfile(this.uuid, "")); + this.fakeProfile = ResolvableProfile.createUnresolved(this.uuid); this.setCanPickUpLoot(true); - this.entityData.set(PlayerAccessor.accessor$DATA_PLAYER_MODE_CUSTOMISATION(), Constants.Sponge.Entity.Human.PLAYER_MODEL_FLAG_ALL); + this.entityData.set(AvatarAccessor.accessor$DATA_PLAYER_MODE_CUSTOMISATION(), Constants.Sponge.Entity.Human.PLAYER_MODEL_FLAG_ALL); } @Override @@ -136,13 +146,15 @@ protected void defineSynchedData(SynchedEntityData.Builder $$0) { $$0.define(LivingEntityAccessor.accessor$DATA_STINGER_COUNT_ID(), 0); $$0.define(LivingEntityAccessor.accessor$SLEEPING_POS_ID(), Optional.empty()); + // Avatar + $$0.define(AvatarAccessor.accessor$DATA_PLAYER_MODE_CUSTOMISATION(), (byte) 0); + $$0.define(AvatarAccessor.accessor$DATA_PLAYER_MAIN_HAND(), (byte) 1); + // Player $$0.define(PlayerAccessor.accessor$DATA_PLAYER_ABSORPTION_ID(), 0.0F); $$0.define(PlayerAccessor.accessor$DATA_SCORE_ID(), 0); - $$0.define(PlayerAccessor.accessor$DATA_PLAYER_MODE_CUSTOMISATION(), (byte) 0); - $$0.define(PlayerAccessor.accessor$DATA_PLAYER_MAIN_HAND(), (byte) 1); - $$0.define(PlayerAccessor.accessor$DATA_SHOULDER_LEFT(), new CompoundTag()); - $$0.define(PlayerAccessor.accessor$DATA_SHOULDER_RIGHT(), new CompoundTag()); + $$0.define(PlayerAccessor.accessor$DATA_SHOULDER_LEFT(), OptionalInt.empty()); + $$0.define(PlayerAccessor.accessor$DATA_SHOULDER_RIGHT(), OptionalInt.empty()); } @Override @@ -188,23 +200,20 @@ public void setCustomName(final net.minecraft.network.chat.@Nullable Component n } @Override - public void readAdditionalSaveData(final CompoundTag tag) { + public void readAdditionalSaveData(final ValueInput tag) { super.readAdditionalSaveData(tag); - if (tag.contains("profile")) { - ResolvableProfile.CODEC - .parse(NbtOps.INSTANCE, tag.get("profile")) - .resultOrPartial($$0x -> SpongeCommon.logger().error("Failed to load profile from player head: {}", $$0x)) - .ifPresent(profile -> this.fakeProfile = profile); - - this.setUUID(this.fakeProfile.id().get()); - } + tag.read("profile", ResolvableProfile.CODEC) + .ifPresent(profile -> { + this.fakeProfile = profile; + this.setUUID(this.fakeProfile.partialProfile().id()); + }); } @Override - public void addAdditionalSaveData(final CompoundTag tag) { + public void addAdditionalSaveData(final ValueOutput tag) { super.addAdditionalSaveData(tag); final DataResult result = ResolvableProfile.CODEC.encodeStart(NbtOps.INSTANCE, this.fakeProfile); - result.ifSuccess(gameProfile -> tag.put("profile", gameProfile)); + result.ifSuccess(gameProfile -> tag.storeNullable("profile", ResolvableProfile.CODEC, this.fakeProfile)); } @Override @@ -295,40 +304,43 @@ public void internalSetAbsorptionAmount(float amount) { } @Override - protected float tickHeadTurn(final float p_110146_1_, final float p_110146_2_) { - final float retValue = super.tickHeadTurn(p_110146_1_, p_110146_2_); + protected void tickHeadTurn(final float p_110146_1_) { + // Must run first, it is the only thing that updates yBodyRot, which the look control clamps yHeadRot against + super.tickHeadTurn(p_110146_1_); // Make the body rotation follow head rotation this.setYRot(this.getYHeadRot()); - return retValue; } @Override protected HoverEvent createHoverEvent() { - return new HoverEvent(HoverEvent.Action.SHOW_ENTITY, new HoverEvent.EntityTooltipInfo(EntityType.PLAYER, this.getUUID(), this.getName())); + return new HoverEvent.ShowEntity(new HoverEvent.EntityTooltipInfo(EntityTypes.PLAYER, this.getUUID(), this.getName())); } private void setProfileName(final net.minecraft.network.chat.@Nullable Component newName) { final Optional optName = Optional.ofNullable(newName).map(net.minecraft.network.chat.Component::getString); - this.fakeProfile = new ResolvableProfile(optName, this.fakeProfile.id(), this.fakeProfile.properties()); + final var profile = new GameProfile(this.fakeProfile.partialProfile().id(), optName.orElse(this.fakeProfile.name().orElse("")), this.fakeProfile.partialProfile().properties()); + this.fakeProfile = ResolvableProfile.createResolved(profile); } public boolean getOrLoadSkin(final UUID minecraftAccount) { - GameProfile gameProfile = SpongeCommon.server().getProfileCache().get(minecraftAccount).orElse(null); - if (gameProfile == null) { - ProfileResult result = - SpongeCommon.server().getSessionService().fetchProfile(minecraftAccount, true); + final var server = SpongeCommon.server(); + final @Nullable GameProfile profile = server.services().nameToIdCache().get(minecraftAccount).flatMap(name -> + server.services().profileResolver().fetchByName(name.name()) + ).orElseGet(() -> { + final @Nullable ProfileResult result = server.services().sessionService().fetchProfile(minecraftAccount, true); if (result == null) { - return false; + return null; } - - gameProfile = result.profile(); - - // TODO Should we put profile cache entries with UUIDs that don't have their names? - - SpongeCommon.server().getProfileCache().add(gameProfile); + server.services().nameToIdCache().add(new NameAndId(result.profile())); + return result.profile(); + }); + if (profile == null) { + return false; } - this.fakeProfile.properties().replaceValues(ProfileProperty.TEXTURES, gameProfile.getProperties().get(ProfileProperty.TEXTURES)); + this.modifyProperties(props -> { + props.replaceValues(ProfileProperty.TEXTURES, profile.properties().get(ProfileProperty.TEXTURES)); + }); if (this.isAliveAndInWorld()) { this.respawnOnClient(); } @@ -338,22 +350,19 @@ public boolean getOrLoadSkin(final UUID minecraftAccount) { public boolean getOrLoadSkin(final String minecraftAccount) { Objects.requireNonNull(minecraftAccount); - GameProfile gameProfile = SpongeCommon.server().getProfileCache().get(minecraftAccount).orElse(null); - if (gameProfile == null) { + final var server = SpongeCommon.server(); + final @Nullable GameProfile profile = server.services().nameToIdCache().get(minecraftAccount).flatMap(name -> + server.services().profileResolver().fetchByName(name.name()) + ).orElse(null); + if (profile == null) { return false; } - if (gameProfile.getProperties().isEmpty()) { - ProfileResult result = SpongeCommon.server().getSessionService().fetchProfile(gameProfile.getId(), true); - if (result == null) { - return false; - } - gameProfile = result.profile(); - SpongeCommon.server().getProfileCache().add(gameProfile); - } + this.modifyProperties(props -> { + props.clear(); + props.putAll(profile.properties()); + }); - this.fakeProfile.properties().clear(); - this.fakeProfile.properties().putAll(gameProfile.getProperties()); if (this.isAliveAndInWorld()) { this.respawnOnClient(); } @@ -376,7 +385,7 @@ public void removeFromTabListDelayed(final @Nullable ServerPlayer player, final } public SpongeProfileProperty getSkinProperty() { - final Collection properties = this.fakeProfile.properties().get(ProfileProperty.TEXTURES); + final Collection properties = this.fakeProfile.partialProfile().properties().get(ProfileProperty.TEXTURES); if (properties.isEmpty()) { return null; } @@ -384,10 +393,10 @@ public SpongeProfileProperty getSkinProperty() { } public void setSkinProperty(final ProfileProperty property) { - this.fakeProfile.properties() - .replaceValues( - ProfileProperty.TEXTURES, - Collections.singletonList(((SpongeProfileProperty) property).asProperty())); + this.modifyProperties(props -> props.replaceValues( + ProfileProperty.TEXTURES, + Collections.singletonList(((SpongeProfileProperty) property).asProperty()) + )); if (this.isAliveAndInWorld()) { this.respawnOnClient(); @@ -400,7 +409,21 @@ private boolean isAliveAndInWorld() { private void respawnOnClient() { this.pushPackets(new ClientboundRemoveEntitiesPacket(this.getId()), this.createPlayerListPacket(EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class))); - this.pushPackets(this.getAddEntityPacket(new ServerEntity((ServerLevel) this.level(), this, 1, true, packet -> {}))); + this.pushPackets(this.getAddEntityPacket(new ServerEntity( + (ServerLevel) this.level(), + this, 1, true, NoOpSynchronizer.INSTANCE + ))); + } + + private void modifyProperties(final Consumer> fn) { + final var mutable = HashMultimap.create(this.fakeProfile.partialProfile().properties()); + fn.accept(mutable); + + this.fakeProfile = ResolvableProfile.createResolved(new GameProfile( + this.fakeProfile.partialProfile().id(), + this.fakeProfile.partialProfile().name(), + new PropertyMap(mutable) + )); } /** @@ -410,7 +433,7 @@ private void respawnOnClient() { * @return Whether it can be removed with 0 ticks delay */ public boolean canRemoveFromListImmediately() { - return !this.fakeProfile.properties().containsKey(ProfileProperty.TEXTURES); + return !this.fakeProfile.partialProfile().properties().containsKey(ProfileProperty.TEXTURES); } /** @@ -435,7 +458,8 @@ public void untrackFrom(final ServerPlayer player) { public ClientboundPlayerInfoUpdatePacket createPlayerListPacket(final EnumSet actions) { final ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(actions, List.of()); - ((ClientboundPlayerInfoUpdatePacketAccessor) packet).accessor$entries(List.of(new ClientboundPlayerInfoUpdatePacket.Entry(this.uuid, this.fakeProfile.gameProfile(), false, 0, GameType.DEFAULT_MODE, this.getDisplayName(), null))); + ((ClientboundPlayerInfoUpdatePacketAccessor) packet).accessor$entries(List.of( + new ClientboundPlayerInfoUpdatePacket.Entry(this.uuid, this.fakeProfile.partialProfile(), false, 0, GameType.DEFAULT_MODE, this.getDisplayName(), false, 0, null))); return packet; } @@ -444,7 +468,7 @@ public ClientboundPlayerInfoUpdatePacket createPlayerListPacket(final EnumSet... packets) { + public void pushPackets(final Packet... packets) { this.pushPackets(null, packets); // null = all players } @@ -455,8 +479,8 @@ public void pushPackets(final Packet... packets) { * @param player The player tracking this human * @param packets All packets to send in a single tick */ - public void pushPackets(final @Nullable ServerPlayer player, final Packet... packets) { - final List>> queue; + public void pushPackets(final @Nullable ServerPlayer player, final Packet... packets) { + final List>> queue; if (player == null) { queue = this.playerPacketMap.computeIfAbsent(null, k -> new ArrayList<>()); } else { @@ -471,8 +495,8 @@ public void pushPackets(final @Nullable ServerPlayer player, final Packet... * @param player The player to get packets for (or null for all players) * @return An array of packets to send in a single tick */ - public Stream> popQueuedPackets(final @Nullable ServerPlayer player) { - final List>> queue = this.playerPacketMap.get(player == null ? null : player.getUUID()); + public Stream> popQueuedPackets(final @Nullable ServerPlayer player) { + final List>> queue = this.playerPacketMap.get(player == null ? null : player.getUUID()); return queue == null || queue.isEmpty() ? Stream.empty() : queue.remove(0); } @@ -494,7 +518,7 @@ public void performRangedAttack(final LivingEntity target, final float distanceF @Override public Packet getAddEntityPacket(ServerEntity $$0) { final ClientboundAddEntityPacket packet = new ClientboundAddEntityPacket(this, $$0); - ((ClientboundAddEntityPacketAccessor) packet).accessor$type(EntityType.PLAYER); + ((ClientboundAddEntityPacketAccessor) packet).accessor$type(EntityTypes.PLAYER); return packet; } } diff --git a/src/main/java/org/spongepowered/common/entity/player/LoginPermissions.java b/src/main/java/org/spongepowered/common/entity/player/LoginPermissions.java index ba9b7cfbcb6..89687b2a5a9 100644 --- a/src/main/java/org/spongepowered/common/entity/player/LoginPermissions.java +++ b/src/main/java/org/spongepowered/common/entity/player/LoginPermissions.java @@ -24,9 +24,11 @@ */ package org.spongepowered.common.entity.player; +import net.minecraft.server.permissions.PermissionLevel; + public final class LoginPermissions { - public static final int BYPASS_WHITELIST_LEVEL = 1; + public static final PermissionLevel BYPASS_WHITELIST_LEVEL = PermissionLevel.MODERATORS; public static final String BYPASS_WHITELIST_PERMISSION = "minecraft.login.bypass-whitelist"; diff --git a/src/main/java/org/spongepowered/common/entity/player/SpongeUserData.java b/src/main/java/org/spongepowered/common/entity/player/SpongeUserData.java index f87d565a588..f95fcb2016d 100644 --- a/src/main/java/org/spongepowered/common/entity/player/SpongeUserData.java +++ b/src/main/java/org/spongepowered/common/entity/player/SpongeUserData.java @@ -27,17 +27,22 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.mojang.authlib.GameProfile; +import net.minecraft.core.Rotations; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtIo; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.ProblemReporter; import net.minecraft.util.datafix.DataFixTypes; import net.minecraft.util.datafix.DataFixers; +import net.minecraft.world.ItemStackWithSlot; import net.minecraft.world.inventory.PlayerEnderChestContainer; import net.minecraft.world.level.Level; import net.minecraft.world.level.storage.LevelResource; import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.TagValueOutput; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; @@ -86,6 +91,7 @@ import org.spongepowered.common.util.FileUtil; import org.spongepowered.common.util.MissingImplementationException; import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3f; import java.io.IOException; import java.io.InputStream; @@ -115,7 +121,7 @@ public final class SpongeUserData implements Identifiable, DataSerializable, Bed private final Map spawnLocations = Maps.newHashMap(); - private ResourceKey worldKey = (ResourceKey) (Object) Level.OVERWORLD.location(); + private ResourceKey worldKey = (ResourceKey) (Object) Level.OVERWORLD.identifier(); private double x; private double y; private double z; @@ -143,7 +149,7 @@ public static SpongeUserData create(final GameProfile profile) throws IOExceptio } final LevelStorageSource.LevelStorageAccess storageSource = ((MinecraftServerAccessor) Sponge.server()).accessor$storageSource(); - final Path p = storageSource.getLevelPath(LevelResource.PLAYER_DATA_DIR).resolve(profile.getId().toString() + ".dat"); + final Path p = storageSource.getLevelPath(LevelResource.PLAYER_DATA_DIR).resolve(profile.id().toString() + ".dat"); if (!Files.exists(p)) { return new SpongeUserData(profile, new CompoundTag()); } @@ -154,7 +160,7 @@ public static SpongeUserData create(final GameProfile profile) throws IOExceptio compound = NbtIo.readCompressed(in, NbtAccounter.unlimitedHeap()); } // See PlayerDataAccess - keep this line up to date. - final int version = compound.contains("DataVersion", 3) ? compound.getInt("DataVersion") : -1; + final int version = compound.getIntOr("DataVersion", -1); DataFixTypes.PLAYER.updateToCurrentVersion(DataFixers.getDataFixer(), compound, version); return new SpongeUserData(profile, compound); } catch (final IOException e) { @@ -203,9 +209,11 @@ public DataHolder.Mutable getDataHolder(final boolean markDirty) { private UserInventory loadInventory() { if (this.inventory == null) { this.inventory = new SpongeUserInventory(this); - final ListTag listNBT = this.compound.getList(Constants.Entity.Player.INVENTORY, 10); - this.inventory.readList(listNBT); - this.inventory.currentItem = this.compound.getInt(Constants.Entity.Player.SELECTED_ITEM_SLOT); + final var input = TagValueInput.create(ProblemReporter.DISCARDING, SpongeCommon.scopedHolder(), this.compound); + final var inventoryInput = input.listOrEmpty(Constants.Entity.Player.INVENTORY, ItemStackWithSlot.CODEC); + this.inventory.readList(inventoryInput); + this.compound.getInt(Constants.Entity.Player.SELECTED_ITEM_SLOT) + .ifPresent(slotId -> this.inventory.currentItem = slotId); } return (UserInventory) this.inventory; } @@ -213,9 +221,10 @@ private UserInventory loadInventory() { private SpongeUserData loadEnderInventory() { if (this.enderChest == null) { this.enderChest = new SpongeUserInventoryEnderchest(this); - if (this.compound.contains(Constants.Entity.Player.ENDERCHEST_INVENTORY, 9)) { - final ListTag nbttaglist1 = this.compound.getList(Constants.Entity.Player.ENDERCHEST_INVENTORY, 10); - this.enderChest.fromTag(nbttaglist1, SpongeCommon.server().registryAccess()); + if (this.compound.contains(Constants.Entity.Player.ENDERCHEST_INVENTORY)) { + final var input = TagValueInput.create(ProblemReporter.DISCARDING, SpongeCommon.scopedHolder(), this.compound); + final var enderChestItems = input.listOrEmpty(Constants.Entity.Player.ENDERCHEST_INVENTORY, ItemStackWithSlot.CODEC); + this.enderChest.fromSlots(enderChestItems); } } return this; @@ -225,16 +234,16 @@ public void readCompound(final CompoundTag compound) { this.reset(); this.compound = compound; - if (!compound.contains(Constants.Sponge.World.DIMENSION)) { - this.worldKey = ResourceKey.resolve(compound.getString(Constants.Sponge.World.DIMENSION)); - } - final ListTag position = compound.getList(Constants.Entity.ENTITY_POSITION, Constants.NBT.TAG_DOUBLE); - final ListTag rotation = compound.getList(Constants.Entity.ENTITY_ROTATION, Constants.NBT.TAG_FLOAT); - this.x = position.getDouble(0); - this.y = position.getDouble(1); - this.z = position.getDouble(2); - this.yaw = rotation.getFloat(0); - this.pitch = rotation.getFloat(1); + compound.read(Constants.Sponge.World.DIMENSION, Identifier.CODEC) + .ifPresent(key -> this.worldKey = (ResourceKey) (Object) key); + + final var rotation = compound.read(Constants.Entity.ENTITY_ROTATION, Rotations.CODEC).orElse(new Rotations(0, 0, 0)); + final var position = compound.read(Constants.Entity.ENTITY_POSITION, Constants.Entity.ENTITY_POSITION_CODEC).orElse(Vector3d.ZERO); + this.x = position.x(); + this.y = position.y(); + this.z = position.z(); + this.yaw = rotation.x(); + this.pitch = rotation.y(); this.isConstructing = true; DataUtil.syncTagToData(this); @@ -242,18 +251,22 @@ public void readCompound(final CompoundTag compound) { } public void writeCompound(final CompoundTag compound) { - - compound.putString(Constants.Sponge.World.DIMENSION, this.worldKey.formatted()); + final var holder = SpongeCommon.scopedHolder(); + final var output = TagValueOutput.createWithContext(ProblemReporter.DISCARDING, holder); + output.putString(Constants.Sponge.World.DIMENSION, this.worldKey.formatted()); this.loadInventory(); this.loadEnderInventory(); - compound.put(Constants.Entity.Player.INVENTORY, this.inventory.writeList(new ListTag())); - compound.put(Constants.Entity.Player.ENDERCHEST_INVENTORY, this.enderChest.createTag(SpongeCommon.server().registryAccess())); - compound.putInt(Constants.Entity.Player.SELECTED_ITEM_SLOT, this.inventory.currentItem); + final var inventory = output.list(Constants.Entity.Player.INVENTORY, ItemStackWithSlot.CODEC); + this.inventory.writeList(inventory); + final var enderChestInventory = output.list(Constants.Entity.Player.ENDERCHEST_INVENTORY, ItemStackWithSlot.CODEC); + this.enderChest.storeAsSlots(enderChestInventory); - compound.put(Constants.Entity.ENTITY_POSITION, Constants.NBT.newDoubleNBTList(this.x, this.y, this.z)); - compound.put(Constants.Entity.ENTITY_ROTATION, Constants.NBT.newFloatNBTList(this.yaw, this.pitch)); + output.putInt(Constants.Entity.Player.SELECTED_ITEM_SLOT, this.inventory.currentItem); + output.store(Constants.Entity.ENTITY_POSITION, Constants.Entity.ENTITY_POSITION_CODEC, new Vector3d(this.x, this.y, this.z)); + output.store(Constants.Entity.ENTITY_ROTATION, Constants.Entity.ROTATIONS_CODEC, new Vector3f(this.yaw, this.pitch, 0)); + compound.merge(output.buildResult()); if (DataUtil.syncDataToTag(this)) { compound.merge(this.data$getCompound()); @@ -262,12 +275,12 @@ public void writeCompound(final CompoundTag compound) { @Override public UUID uniqueId() { - return this.profile.getId(); + return this.profile.id(); } @Override public String name() { - return this.profile.getName(); + return this.profile.name(); } @Override @@ -280,8 +293,8 @@ public DataContainer toContainer() { // TODO More data return DataContainer.createNew() .set(Queries.CONTENT_VERSION, this.contentVersion()) - .set(Constants.Entity.Player.UUID, this.profile.getId()) - .set(Constants.Entity.Player.NAME, this.profile.getName()) + .set(Constants.Entity.Player.UUID, this.profile.id()) + .set(Constants.Entity.Player.NAME, this.profile.name()) .set(Constants.Entity.Player.SPAWNS, this.spawnLocations); } @@ -471,7 +484,7 @@ public boolean isOnline() { @Override public Optional player() { - return Optional.ofNullable((ServerPlayer) SpongeCommon.server().getPlayerList().getPlayer(this.profile.getId())); + return Optional.ofNullable((ServerPlayer) SpongeCommon.server().getPlayerList().getPlayer(this.profile.id())); } @Override @@ -599,12 +612,12 @@ public boolean equals(final Object obj) { return false; } final SpongeUserData other = (SpongeUserData) obj; - return this.profile.getId().equals(other.profile.getId()); + return this.profile.id().equals(other.profile.id()); } @Override public int hashCode() { - return this.profile.getId().hashCode(); + return this.profile.id().hashCode(); } @Override diff --git a/src/main/java/org/spongepowered/common/entity/player/SpongeUserInventory.java b/src/main/java/org/spongepowered/common/entity/player/SpongeUserInventory.java index 66d5d6c6595..fa199c1b852 100644 --- a/src/main/java/org/spongepowered/common/entity/player/SpongeUserInventory.java +++ b/src/main/java/org/spongepowered/common/entity/player/SpongeUserInventory.java @@ -26,12 +26,14 @@ import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; +import net.minecraft.world.ItemStackWithSlot; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import org.spongepowered.common.SpongeCommon; import java.util.Arrays; @@ -134,57 +136,55 @@ public void setItem(int index, final ItemStack stack) { * Writes the inventory out as a list of compound tags. This is where the slot indices are used (+100 for armor, +80 * for crafting). */ - public ListTag writeList(final ListTag nbtTagListIn) { + public void writeList(final ValueOutput.TypedOutputList nbtTagListIn) { for (int i = 0; i < this.mainInventory.size(); ++i) { - if (!this.mainInventory.get(i).isEmpty()) { - final CompoundTag nbttagcompound = new CompoundTag(); - nbttagcompound.putByte("Slot", (byte) i); - nbtTagListIn.add(this.mainInventory.get(i).save(SpongeCommon.server().registryAccess(), nbttagcompound)); + final var stack = this.mainInventory.get(i); + if (!stack.isEmpty()) { + final var slottedItem = new ItemStackWithSlot(i, stack); + nbtTagListIn.add(slottedItem); } } for (int j = 0; j < this.armorInventory.size(); ++j) { - if (!this.armorInventory.get(j).isEmpty()) { + final var armorItem = this.armorInventory.get(j); + if (!armorItem.isEmpty()) { final CompoundTag nbttagcompound1 = new CompoundTag(); nbttagcompound1.putByte("Slot", (byte) (j + 100)); - nbtTagListIn.add(this.armorInventory.get(j).save(SpongeCommon.server().registryAccess(), nbttagcompound1)); + final var slottedItem = new ItemStackWithSlot(j + 100, armorItem); + nbtTagListIn.add(slottedItem); } } for (int k = 0; k < this.offHandInventory.size(); ++k) { - if (!this.offHandInventory.get(k).isEmpty()) { + final var offhandItem = this.offHandInventory.get(k); + if (!offhandItem.isEmpty()) { final CompoundTag nbttagcompound2 = new CompoundTag(); nbttagcompound2.putByte("Slot", (byte) (k + 150)); - nbtTagListIn.add(this.offHandInventory.get(k).save(SpongeCommon.server().registryAccess(), nbttagcompound2)); + nbtTagListIn.add(new ItemStackWithSlot(k + 150, offhandItem)); } } this.dirty = false; - - return nbtTagListIn; } /** * Reads from the given tag list and fills the slots in the inventory with the correct items. */ - public void readList(final ListTag nbtTagListIn) { + public void readList(final ValueInput.TypedInputList nbtTagListIn) { this.mainInventory.clear(); this.armorInventory.clear(); this.offHandInventory.clear(); - for (int i = 0; i < nbtTagListIn.size(); ++i) { - final CompoundTag nbttagcompound = nbtTagListIn.getCompound(i); - final int j = nbttagcompound.getByte("Slot") & 255; - final ItemStack itemstack = ItemStack.parse(SpongeCommon.server().registryAccess(), nbttagcompound).get(); - - if (!itemstack.isEmpty()) { - if (j >= 0 && j < this.mainInventory.size()) { - this.mainInventory.set(j, itemstack); - } else if (j >= 100 && j < this.armorInventory.size() + 100) { - this.armorInventory.set(j - 100, itemstack); - } else if (j >= 150 && j < this.offHandInventory.size() + 150) { - this.offHandInventory.set(j - 150, itemstack); - } + for (final var input : nbtTagListIn) { + final var slot = input.slot(); + if (slot >= 0 && slot < this.mainInventory.size()) { + this.mainInventory.set(slot, input.stack()); + } else if (slot >= 100 && slot < this.armorInventory.size() + 100) { + this.armorInventory.set(slot - 100, input.stack()); + } else if (slot >= 150 && slot < this.offHandInventory.size() + 150) { + this.offHandInventory.set(slot - 150, input.stack()); + } else { + SpongeCommon.logger().warn("Skipping Slot {} as it does not exist in the inventory", slot); } } } diff --git a/src/main/java/org/spongepowered/common/entity/player/tab/SpongeTabList.java b/src/main/java/org/spongepowered/common/entity/player/tab/SpongeTabList.java index 1b13da43b33..36f8cc2d663 100644 --- a/src/main/java/org/spongepowered/common/entity/player/tab/SpongeTabList.java +++ b/src/main/java/org/spongepowered/common/entity/player/tab/SpongeTabList.java @@ -153,6 +153,7 @@ private void addEntry(final ClientboundPlayerInfoUpdatePacket.Entry entry) { entry.latency(), (GameMode) (Object) entry.gameMode(), entry.listed(), + entry.listOrder(), entry.chatSession() == null ? null : entry.chatSession().profilePublicKey() ); }); @@ -179,11 +180,10 @@ public Optional removeEntry(final UUID uniqueId) { @SuppressWarnings("ConstantConditions") void sendUpdate(final TabListEntry entry, final EnumSet actions) { final ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(actions, List.of()); - final RemoteChatSession.Data chatSessionData = ((SpongeTabListEntry) entry).profilePublicKey() == null ? null : new RemoteChatSession.Data(entry.profile().uuid(), ((SpongeTabListEntry) entry).profilePublicKey()); final net.minecraft.network.chat.Component displayName = entry.displayName().isPresent() ? SpongeAdventure.asVanilla(entry.displayName().get()) : null; final ClientboundPlayerInfoUpdatePacket.Entry data = new ClientboundPlayerInfoUpdatePacket.Entry(entry.profile().uniqueId(), SpongeGameProfile.toMcProfile(entry.profile()), - entry.listed(), entry.latency(), (GameType) (Object) entry.gameMode(), displayName, chatSessionData); + entry.listed(), entry.latency(), (GameType) (Object) entry.gameMode(), displayName, false, entry.weight(), chatSessionData); ((ClientboundPlayerInfoUpdatePacketAccessor) packet).accessor$entries(List.of(data)); this.connection.send(packet); } diff --git a/src/main/java/org/spongepowered/common/entity/player/tab/SpongeTabListEntry.java b/src/main/java/org/spongepowered/common/entity/player/tab/SpongeTabListEntry.java index 355ada3d91b..566f25f008e 100644 --- a/src/main/java/org/spongepowered/common/entity/player/tab/SpongeTabListEntry.java +++ b/src/main/java/org/spongepowered/common/entity/player/tab/SpongeTabListEntry.java @@ -52,12 +52,13 @@ public final class SpongeTabListEntry implements TabListEntry { private int latency; private GameMode gameMode; private boolean listed; + private int weight; private boolean updateWithoutSend; private final ProfilePublicKey.Data profilePublicKey; public SpongeTabListEntry( final TabList list, final GameProfile profile, @Nullable final Component displayName, final int latency, final GameMode gameMode, - final boolean listed, final ProfilePublicKey.Data profilePublicKey) { + final boolean listed, final int weight, final ProfilePublicKey.Data profilePublicKey) { Preconditions.checkState(list instanceof SpongeTabList, "list is not a SpongeTabList"); this.list = (SpongeTabList) list; this.profile = Objects.requireNonNull(profile, "profile"); @@ -65,6 +66,7 @@ public SpongeTabListEntry( this.latency = latency; this.gameMode = Objects.requireNonNull(gameMode, "game mode"); this.listed = listed; + this.weight = weight; this.profilePublicKey = profilePublicKey; } @@ -126,6 +128,18 @@ public SpongeTabListEntry setListed(boolean listed) { return this; } + @Override + public int weight() { + return this.weight; + } + + @Override + public SpongeTabListEntry setWeight(int weight) { + this.weight = weight; + this.sendUpdate(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER); + return this; + } + public ProfilePublicKey.Data profilePublicKey() { return this.profilePublicKey; } diff --git a/src/main/java/org/spongepowered/common/entity/player/tab/TabListEntryBuilder.java b/src/main/java/org/spongepowered/common/entity/player/tab/TabListEntryBuilder.java index cb974eb6caf..3c1e136d1cd 100644 --- a/src/main/java/org/spongepowered/common/entity/player/tab/TabListEntryBuilder.java +++ b/src/main/java/org/spongepowered/common/entity/player/tab/TabListEntryBuilder.java @@ -44,6 +44,7 @@ public final class TabListEntryBuilder implements TabListEntry.Builder { private int latency; private boolean listed = true; private @Nullable GameMode gameMode; + private int weight; @Override public TabListEntry.Builder list(TabList list) { @@ -81,13 +82,19 @@ public TabListEntry.Builder listed(boolean listed) { return this; } + @Override + public TabListEntry.Builder weight(int weight) { + this.weight = weight; + return this; + } + @Override public TabListEntry build() { Preconditions.checkState(this.list != null, "list must be set"); Preconditions.checkState(this.profile != null, "profile must be set"); Preconditions.checkState(this.gameMode != null, "game mode must be set"); - return new SpongeTabListEntry(this.list, this.profile, this.displayName, this.latency, this.gameMode, this.listed, null); + return new SpongeTabListEntry(this.list, this.profile, this.displayName, this.latency, this.gameMode, this.listed, this.weight, null); } @Override @@ -97,6 +104,7 @@ public TabListEntry.Builder from(TabListEntry value) { this.displayName = value.displayName().orElse(null); this.latency = value.latency(); this.gameMode = Objects.requireNonNull(value.gameMode(), "game mode"); + this.weight = value.weight(); return this; } @@ -107,6 +115,7 @@ public TabListEntry.Builder reset() { this.displayName = null; this.latency = 0; this.gameMode = null; + this.weight = 0; return this; } diff --git a/src/main/java/org/spongepowered/common/entity/projectile/DispenserSourceLogic.java b/src/main/java/org/spongepowered/common/entity/projectile/DispenserSourceLogic.java index 7aa7c741042..58e29308780 100644 --- a/src/main/java/org/spongepowered/common/entity/projectile/DispenserSourceLogic.java +++ b/src/main/java/org/spongepowered/common/entity/projectile/DispenserSourceLogic.java @@ -57,7 +57,7 @@ public

Optional

launch(final ProjectileLogic

logic, if (projectile.isPresent()) { final Direction enumfacing = DispenserSourceLogic.getFacing((DispenserBlockEntity) source); final net.minecraft.world.entity.Entity projectileEntity = (net.minecraft.world.entity.Entity) projectile.get(); - final BlockPos adjustedPosition = projectileEntity.blockPosition().offset(enumfacing.getNormal()); + final BlockPos adjustedPosition = projectileEntity.blockPosition().offset(enumfacing.getUnitVec3i()); projectileEntity.setPos(adjustedPosition.getX(), adjustedPosition.getY(), adjustedPosition.getZ()); } return projectile; diff --git a/src/main/java/org/spongepowered/common/entity/projectile/ProjectileUtil.java b/src/main/java/org/spongepowered/common/entity/projectile/ProjectileUtil.java index 8d73b2dde50..83883aaf397 100644 --- a/src/main/java/org/spongepowered/common/entity/projectile/ProjectileUtil.java +++ b/src/main/java/org/spongepowered/common/entity/projectile/ProjectileUtil.java @@ -28,17 +28,18 @@ import net.minecraft.core.Direction; import net.minecraft.util.Mth; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.animal.horse.Llama; +import net.minecraft.world.entity.animal.equine.Llama; import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.FireworkRocketEntity; import net.minecraft.world.entity.projectile.FishingHook; -import net.minecraft.world.entity.projectile.LargeFireball; import net.minecraft.world.entity.projectile.ThrowableProjectile; -import net.minecraft.world.entity.projectile.ThrownEgg; -import net.minecraft.world.entity.projectile.ThrownEnderpearl; -import net.minecraft.world.entity.projectile.ThrownExperienceBottle; -import net.minecraft.world.entity.projectile.ThrownPotion; +import net.minecraft.world.entity.projectile.hurtingprojectile.LargeFireball; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownEgg; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownEnderpearl; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownExperienceBottle; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownLingeringPotion; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownSplashPotion; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.entity.DispenserBlockEntity; @@ -178,7 +179,7 @@ static

Optional

doLaunch(final World extent, final P p @Override protected Optional createProjectile(final LivingEntity source, final ServerLocation loc) { - final var arrow = new net.minecraft.world.entity.projectile.Arrow(source.level(), source, new ItemStack(this.item), source.getWeaponItem()); + final var arrow = new net.minecraft.world.entity.projectile.arrow.Arrow(source.level(), source, new ItemStack(this.item), source.getWeaponItem()); arrow.shoot(source.getXRot(), source.getYRot(), 0.0F, 3.0F, 0); return ProjectileUtil.doLaunch(loc.world(), (Arrow) arrow); } @@ -188,7 +189,7 @@ protected Optional createProjectile(final LivingEntity source, final Serv @Override protected Optional createProjectile(final LivingEntity source, final ServerLocation loc) { - final var arrow = new net.minecraft.world.entity.projectile.SpectralArrow(source.level(), source, new ItemStack(this.item), source.getWeaponItem()); + final var arrow = new net.minecraft.world.entity.projectile.arrow.SpectralArrow(source.level(), source, new ItemStack(this.item), source.getWeaponItem()); arrow.shoot(source.getXRot(), source.getYRot(), 0.0F, 3.0F, 0); return ProjectileUtil.doLaunch(loc.world(), (SpectralArrow) arrow); } @@ -197,7 +198,7 @@ protected Optional createProjectile(final LivingEntity source, fi @Override protected Optional createProjectile(final LivingEntity source, final ServerLocation loc) { - final ThrownEgg egg = new ThrownEgg(source.level(), source); + final ThrownEgg egg = new ThrownEgg(source.level(), source, new ItemStack(Items.EGG)); egg.shoot(source.getXRot(), source.getYRot(), 0.0F, 1.5F, 0); return ProjectileUtil.doLaunch(loc.world(), (Egg) egg); } @@ -208,7 +209,7 @@ protected Optional createProjectile(final LivingEntity source, final Server @Override protected Optional createProjectile(final LivingEntity source, final ServerLocation loc) { final net.minecraft.world.phys.Vec3 lookVec = source.getViewVector(1); - final var fireball = new net.minecraft.world.entity.projectile.SmallFireball(source.level(), source, lookVec.scale(4)); + final var fireball = new net.minecraft.world.entity.projectile.hurtingprojectile.SmallFireball(source.level(), source, lookVec.scale(4)); fireball.setPos(fireball.getX(), fireball.getY() + source.getEyeHeight(), fireball.getZ()); return ProjectileUtil.doLaunch(loc.world(), (SmallFireball) fireball); } @@ -226,7 +227,8 @@ protected Optional createProjectile(final LivingEntity source, f @Override protected Optional createProjectile(final LivingEntity source, final ServerLocation loc) { - final net.minecraft.world.entity.projectile.Snowball snowball = new net.minecraft.world.entity.projectile.Snowball(source.level(), source); + final var snowball = new net.minecraft.world.entity.projectile.throwableitemprojectile.Snowball( + source.level(), source, new ItemStack(Items.SNOWBALL)); snowball.shoot(source.getXRot(), source.getYRot(), 0.0F, 1.5F, 0); return ProjectileUtil.doLaunch(loc.world(), (Snowball) snowball); } @@ -236,7 +238,7 @@ protected Optional createProjectile(final LivingEntity source, final S @Override protected Optional createProjectile(final LivingEntity source, final ServerLocation loc) { - final ThrownExperienceBottle expBottle = new ThrownExperienceBottle(source.level(), source); + final ThrownExperienceBottle expBottle = new ThrownExperienceBottle(source.level(), source, new ItemStack(Items.EXPERIENCE_BOTTLE)); expBottle.shoot(source.getXRot(), source.getYRot(), -20.0F, 0.7F, 0); return ProjectileUtil.doLaunch(loc.world(), (ExperienceBottle) expBottle); } @@ -247,7 +249,7 @@ protected Optional createProjectile(final LivingEntity source, @Override protected Optional createProjectile(final LivingEntity source, final ServerLocation loc) { - final ThrownEnderpearl pearl = new ThrownEnderpearl(source.level(), source); + final ThrownEnderpearl pearl = new ThrownEnderpearl(source.level(), source, new ItemStack(Items.ENDER_PEARL)); pearl.shoot(source.getXRot(), source.getYRot(), 0.0F, 1.5F, 0); return ProjectileUtil.doLaunch(loc.world(), (EnderPearl) pearl); } @@ -283,7 +285,7 @@ public Optional createProjectile(final ProjectileSource sourc @Override protected Optional createProjectile(final LivingEntity source, final ServerLocation loc) { final net.minecraft.world.phys.Vec3 lookVec = source.getViewVector(1); - final var skull = new net.minecraft.world.entity.projectile.WitherSkull(source.level(), source, lookVec.scale(4)); + final var skull = new net.minecraft.world.entity.projectile.hurtingprojectile.WitherSkull(source.level(), source, lookVec.scale(4)); skull.setPos(skull.getX(), skull.getY() + source.getEyeHeight(), skull.getZ()); return ProjectileUtil.doLaunch(loc.world(), (WitherSkull) skull); } @@ -302,16 +304,26 @@ protected Optional createProjectile(final LivingEntity source, fi return super.createProjectile(source, loc); } }); - ProjectileUtil.registerProjectileLogic(EntityTypes.POTION, new SimpleItemLaunchLogic(EntityTypes.POTION, Items.SPLASH_POTION) { + ProjectileUtil.registerProjectileLogic(EntityTypes.SPLASH_POTION, new SimpleItemLaunchLogic(EntityTypes.SPLASH_POTION, Items.SPLASH_POTION) { @Override protected Optional createProjectile(final LivingEntity source, final ServerLocation loc) { - final ThrownPotion potion = new ThrownPotion(source.level(), source); + final ThrownSplashPotion potion = new ThrownSplashPotion(source.level(), source, new ItemStack(this.item)); potion.setItem(new ItemStack(Items.SPLASH_POTION, 1)); potion.shoot(source.getXRot(), source.getYRot(), -20.0F, 0.5F, 0); return ProjectileUtil.doLaunch(loc.world(), (Potion) potion); } }); + ProjectileUtil.registerProjectileLogic(EntityTypes.LINGERING_POTION, new SimpleItemLaunchLogic(EntityTypes.SPLASH_POTION, Items.SPLASH_POTION) { + + @Override + protected Optional createProjectile(final LivingEntity source, final ServerLocation loc) { + final ThrownLingeringPotion potion = new ThrownLingeringPotion(source.level(), source, new ItemStack(this.item)); + potion.setItem(new ItemStack(Items.LINGERING_POTION, 1)); + potion.shoot(source.getXRot(), source.getYRot(), -20.0F, 0.5F, 0); + return ProjectileUtil.doLaunch(loc.world(), (Potion) potion); + } + }); ProjectileUtil.registerProjectileLogic(EntityTypes.LLAMA_SPIT, new SimpleEntityLaunchLogic(EntityTypes.LLAMA_SPIT) { @Override @@ -338,7 +350,7 @@ public Optional createProjectile(final ProjectileSource source, @Override protected Optional createProjectile(final LivingEntity source, final ServerLocation loc) { final net.minecraft.world.phys.Vec3 lookVec = source.getViewVector(1); - final var fireball = new net.minecraft.world.entity.projectile.DragonFireball(source.level(), source, lookVec.scale(4)); + final var fireball = new net.minecraft.world.entity.projectile.hurtingprojectile.DragonFireball(source.level(), source, lookVec.scale(4)); fireball.setPos(fireball.getX(), fireball.getY() + source.getEyeHeight(), fireball.getZ()); return ProjectileUtil.doLaunch(loc.world(), (DragonFireball) fireball); } diff --git a/src/main/java/org/spongepowered/common/entity/projectile/ShulkerSourceLogic.java b/src/main/java/org/spongepowered/common/entity/projectile/ShulkerSourceLogic.java index 35ac80ca2a2..4546363c473 100644 --- a/src/main/java/org/spongepowered/common/entity/projectile/ShulkerSourceLogic.java +++ b/src/main/java/org/spongepowered/common/entity/projectile/ShulkerSourceLogic.java @@ -46,7 +46,7 @@ public

Optional

launch(final ProjectileLogic

logic, new ShulkerBullet(shulker.level(), shulker, (Entity) args[0], shulker.getAttachFace().getAxis()); shulker.level().addFreshEntity(bullet); shulker.playSound(SoundEvents.SHULKER_SHOOT, - 2.0F, (shulker.level().random.nextFloat() - shulker.level().random.nextFloat()) * 0.2F + 1.0F); + 2.0F, (shulker.level().getRandom().nextFloat() - shulker.level().getRandom().nextFloat()) * 0.2F + 1.0F); return Optional.of((P) bullet); } diff --git a/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java b/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java index 13e54889876..2ce277a9e91 100644 --- a/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java +++ b/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java @@ -36,12 +36,12 @@ import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.DirectionalBlock; import net.minecraft.world.level.block.entity.JukeboxBlockEntity; import net.minecraft.world.level.block.piston.PistonStructureResolver; +import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.saveddata.maps.MapId; import net.minecraft.world.level.saveddata.maps.MapIndex; import net.minecraft.world.level.saveddata.maps.MapItemSavedData; @@ -63,7 +63,6 @@ import org.spongepowered.api.effect.sound.music.MusicDisc; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.EntityTypes; -import org.spongepowered.api.entity.explosive.Explosive; import org.spongepowered.api.entity.living.Agent; import org.spongepowered.api.entity.living.Living; import org.spongepowered.api.entity.living.player.Player; @@ -85,7 +84,6 @@ import org.spongepowered.api.event.entity.MoveEntityEvent; import org.spongepowered.api.event.entity.RotateEntityEvent; import org.spongepowered.api.event.entity.ai.SetAITargetEvent; -import org.spongepowered.api.event.entity.explosive.DetonateExplosiveEvent; import org.spongepowered.api.event.item.inventory.DropItemEvent; import org.spongepowered.api.event.item.inventory.InteractItemEvent; import org.spongepowered.api.event.sound.PlaySoundEvent; @@ -97,13 +95,11 @@ import org.spongepowered.api.world.DefaultWorldKeys; import org.spongepowered.api.world.LocatableBlock; import org.spongepowered.api.world.World; -import org.spongepowered.api.world.explosion.Explosion; import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.CreatorTrackedBridge; -import org.spongepowered.common.bridge.explosives.ExplosiveBridge; import org.spongepowered.common.bridge.map.MapIdTrackerBridge; import org.spongepowered.common.bridge.server.level.ServerLevelBridge; import org.spongepowered.common.bridge.world.TrackedWorldBridge; @@ -116,7 +112,6 @@ import org.spongepowered.common.entity.projectile.UnknownProjectileSource; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.event.tracking.phase.general.GeneralPhase; import org.spongepowered.common.item.util.ItemStackUtil; import org.spongepowered.common.map.SpongeMapStorage; import org.spongepowered.common.registry.provider.DirectionFacingProvider; @@ -302,10 +297,10 @@ public static InteractItemEvent.Primary callInteractItemEventPrimary(final net.m } } - public static InteractItemEvent.Secondary callInteractItemEventSecondary(final net.minecraft.world.entity.player.Player player, final ItemStack stack, final InteractionHand hand) { + public static InteractItemEvent.Secondary.Pre callInteractItemEventSecondary(final net.minecraft.world.entity.player.Player player, final ItemStack stack, final InteractionHand hand) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { SpongeCommonEventFactory.applyCommonInteractContext(player, stack, hand, null, null, frame); - final InteractItemEvent.Secondary event = SpongeEventFactory.createInteractItemEventSecondary(frame.currentCause(), ItemStackUtil.snapshotOf(stack)); + final var event = SpongeEventFactory.createInteractItemEventSecondaryPre(frame.currentCause(), ItemStackUtil.snapshotOf(stack)); SpongeCommon.post(event); return event; } @@ -356,10 +351,10 @@ public static InteractBlockEvent.Primary.Finish callInteractBlockEventPrimaryFin } } - public static InteractBlockEvent.Secondary callInteractBlockEventSecondary(final net.minecraft.world.entity.player.Player player, final ItemStack heldItem, final Vector3d hitVec, final BlockSnapshot targetBlock, final Direction targetSide, final InteractionHand hand) { + public static InteractBlockEvent.Secondary.Pre callInteractBlockEventSecondary(final net.minecraft.world.entity.player.Player player, final ItemStack heldItem, final Vector3d hitVec, final BlockSnapshot targetBlock, final Direction targetSide, final InteractionHand hand) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { SpongeCommonEventFactory.applyCommonInteractContext(player, heldItem, hand, targetBlock, null, frame); - final InteractBlockEvent.Secondary event = SpongeEventFactory.createInteractBlockEventSecondary(frame.currentCause(), + final InteractBlockEvent.Secondary.Pre event = SpongeEventFactory.createInteractBlockEventSecondaryPre(frame.currentCause(), Tristate.UNDEFINED, Tristate.UNDEFINED, Tristate.UNDEFINED, Tristate.UNDEFINED, targetBlock, hitVec, targetSide); SpongeCommon.post(event); @@ -413,9 +408,9 @@ public static void callNaturalMoveEntityEvent(final net.minecraft.world.entity.E originalToPosition); if (finalPosition == null) { - entity.moveTo(entity.xOld, entity.yOld, entity.zOld); + entity.snapTo(entity.xOld, entity.yOld, entity.zOld); } else if (!finalPosition.equals(originalToPosition)) { - entity.moveTo(finalPosition.x(), finalPosition.y(), finalPosition.z()); + entity.snapTo(finalPosition.x(), finalPosition.y(), finalPosition.z()); } } } @@ -520,7 +515,7 @@ public static DestructEntityEvent.Death callDestructEntityEventDeath(final Livin final DestructEntityEvent.Death event = SpongeEventFactory.createDestructEntityEventDeath(frame.currentCause(), originalChannel, Optional.of(originalChannel), originalMessage, originalMessage, (Living) entity, - entity.level().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY), messageCancelled); + ((ServerLevel) entity.level()).getGameRules().get(GameRules.KEEP_INVENTORY), messageCancelled); SpongeCommon.post(event); return event; @@ -535,41 +530,30 @@ public enum CollisionType public static boolean handleCollideBlockEvent(final Block block, final Level world, final BlockPos pos, final net.minecraft.world.level.block.state.BlockState state, final net.minecraft.world.entity.Entity entity, final Direction direction, final CollisionType type) { - if (world.isClientSide) { - return false; - } - - if (world.isClientSide() || pos.getY() < world.getMinBuildHeight()) { + if (world.isClientSide() || pos.getY() < world.getMinY()) { return false; } try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(entity); - if (entity instanceof CreatorTrackedBridge) { - final CreatorTrackedBridge spongeEntity = (CreatorTrackedBridge) entity; + if (entity instanceof CreatorTrackedBridge spongeEntity) { spongeEntity.tracker$getCreatorUUID().ifPresent(user -> frame.addContext(EventContextKeys.CREATOR, user)); } // TODO: Add target side support final ServerLocation loc = ServerLocation.of((ServerWorld) world, VecHelper.toVector3d(pos)); - final CollideBlockEvent event; - switch (type) { - case MOVE: - event = SpongeEventFactory.createCollideBlockEventMove(frame.currentCause(), (BlockState) state, loc, direction); - break; - case FALL: - event = SpongeEventFactory.createCollideBlockEventFall(frame.currentCause(), (BlockState) state, loc, direction); - break; - case STEP_ON: - event = SpongeEventFactory.createCollideBlockEventStepOn(frame.currentCause(), (BlockState) state, loc, direction); - break; - case INSIDE: - event = SpongeEventFactory.createCollideBlockEventInside(frame.currentCause(), (BlockState) state, loc, direction); - break; - default: - throw new IllegalArgumentException("Unknown type " + type); - } + final CollideBlockEvent event = switch (type) { + case MOVE -> + SpongeEventFactory.createCollideBlockEventMove(frame.currentCause(), (BlockState) state, loc, direction); + case FALL -> + SpongeEventFactory.createCollideBlockEventFall(frame.currentCause(), (BlockState) state, loc, direction); + case STEP_ON -> + SpongeEventFactory.createCollideBlockEventStepOn(frame.currentCause(), (BlockState) state, loc, direction); + case INSIDE -> + SpongeEventFactory.createCollideBlockEventInside(frame.currentCause(), (BlockState) state, loc, direction); + default -> throw new IllegalArgumentException("Unknown type " + type); + }; final boolean cancelled = SpongeCommon.post(event); if (!cancelled) { final EntityBridge spongeEntity = (EntityBridge) entity; @@ -635,24 +619,6 @@ public static SetAITargetEvent callSetAttackTargetEvent(final @Nullable Entity t return event; } - public static Optional detonateExplosive(final ExplosiveBridge explosiveBridge, final Explosion.Builder builder) { - final DetonateExplosiveEvent event = SpongeEventFactory.createDetonateExplosiveEvent( - PhaseTracker.getInstance().currentCause(), builder, (Explosive) explosiveBridge, builder.build() - ); - if (!Sponge.eventManager().post(event)) { - final Explosion explosion = event.explosionBuilder().build(); - if (explosion.radius() > 0) { - ((TrackedWorldBridge) ((Explosive) explosiveBridge).world()) - .tracker$triggerInternalExplosion( - explosion, - e -> GeneralPhase.State.EXPLOSION.createPhaseContext(PhaseTracker.SERVER).explosion(e) - ); - } - return Optional.of((net.minecraft.world.level.Explosion) explosion); - } - return Optional.empty(); - } - /** * @author gabizou - April 19th, 2018 * Creates two events here: @@ -749,14 +715,14 @@ public static PlaySoundEvent.AtEntity callPlaySoundAtEntityEvent(final Cause cau final SoundEvent name, final float pitch, final float volume) { final ServerLocation location = ServerLocation.of((ServerWorld) worldMixin, x, y, z); final PlaySoundEvent.AtEntity event = SpongeEventFactory.createPlaySoundEventAtEntity(cause, location, - Optional.ofNullable((ServerPlayer) entity), SpongeAdventure.asAdventure(category), (SoundType) name, pitch, volume); + Optional.ofNullable((ServerPlayer) entity), SpongeAdventure.asAdventure(category), (SoundType) (Object) name, pitch, volume); SpongeCommon.post(event); return event; } public static PlaySoundEvent.NoteBlock callPlaySoundNoteBlockEvent(final Cause cause, final World world, final BlockPos pos, final SoundEvent soundEvent, final InstrumentType instrument, final NotePitch notePitch, final Float pitch) { final ServerLocation location = ServerLocation.of((ServerWorld) world, pos.getX(), pos.getY(), pos.getZ()); - final PlaySoundEvent.NoteBlock event = SpongeEventFactory.createPlaySoundEventNoteBlock(cause, instrument, location, notePitch, Sound.Source.RECORD, (SoundType)soundEvent, pitch, 3.0F); + final PlaySoundEvent.NoteBlock event = SpongeEventFactory.createPlaySoundEventNoteBlock(cause, instrument, location, notePitch, Sound.Source.RECORD, (SoundType) (Object) soundEvent, pitch, 3.0F); SpongeCommon.post(event); return event; } @@ -775,7 +741,7 @@ public static Optional fireCreateMapEvent(final Cause cause, final Set< final ServerLevel defaultWorld = (ServerLevel) Sponge.server().worldManager().world(DefaultWorldKeys.DEFAULT).get(); final MapIdTrackerBridge mapIdTrackerBridge = (MapIdTrackerBridge) defaultWorld.getDataStorage() - .computeIfAbsent(MapIndex.factory(), Constants.Map.MAP_INDEX_DATA_NAME); + .computeIfAbsent(MapIndex.TYPE); final MapItemSavedData mapData = MapItemSavedData.createFresh(0, 0, (byte) Constants.Map.DEFAULT_MAP_SCALE, Constants.Map.DEFAULT_TRACKS_PLAYERS, Constants.Map.DEFAULT_UNLIMITED_TRACKING, Level.END); diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeAttackTracker.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeAttackTracker.java new file mode 100644 index 00000000000..ba70165a969 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeAttackTracker.java @@ -0,0 +1,123 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.cause.entity.damage; + +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.item.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; +import org.spongepowered.api.event.entity.AttackEntityEvent; +import org.spongepowered.api.event.entity.DamageCalculationEvent; +import org.spongepowered.api.item.inventory.ItemStackSnapshot; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.bridge.world.entity.TrackedAttackBridge; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.item.util.ItemStackUtil; + +public class SpongeAttackTracker extends SpongeDamageTracker { + private final ItemStack weapon; + private final ItemStackSnapshot weaponSnapshot; + + private float attackStrength; + private boolean strongSprint = false; + + public SpongeAttackTracker(final DamageCalculationEvent.Pre preEvent, final DamageSource source, final ItemStack weapon) { + super(preEvent, source); + this.weapon = weapon; + this.weaponSnapshot = ItemStackUtil.snapshotOf(weapon); + } + + @Override + public AttackEntityEvent.Pre preEvent() { + return (AttackEntityEvent.Pre) super.preEvent(); + } + + @Override + public AttackEntityEvent.Post postEvent() { + return (AttackEntityEvent.Post) super.postEvent(); + } + + public ItemStack weapon() { + return this.weapon; + } + + public ItemStackSnapshot weaponSnapshot() { + return this.weaponSnapshot; + } + + public float attackStrength() { + return this.attackStrength; + } + + public void setAttackStrength(final float attackStrength) { + this.attackStrength = attackStrength; + } + + public boolean isStrongSprint() { + return this.strongSprint; + } + + public void setStrongSprint(final boolean strongSprint) { + this.strongSprint = strongSprint; + } + + public boolean callAttackPostEvent(final Entity entity, final DamageSource source, float finalDamage, final float knockbackModifier) { + if (this.postEvent != null) { + throw new IllegalStateException("Post event already fired"); + } + + finalDamage = (float) this.newStep(DamageStepTypes.END).apply(finalDamage); + + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + SpongeDamageTracker.generateCauseFor(source, frame); + + final AttackEntityEvent.Post event = SpongeEventFactory.createAttackEntityEventPost(frame.currentCause(), + knockbackModifier, knockbackModifier, entity, this, this.preEvent.baseDamage(), finalDamage); + + this.postEvent = event; + return SpongeCommon.post(event); + } + } + + public static @Nullable SpongeAttackTracker callAttackPreEvent(final Entity entity, final DamageSource source, final float baseDamage, final ItemStack weapon) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + SpongeDamageTracker.generateCauseFor(source, frame); + + final AttackEntityEvent.Pre event = SpongeEventFactory.createAttackEntityEventPre(frame.currentCause(), entity, baseDamage); + if (SpongeCommon.post(event)) { + return null; + } + + return new SpongeAttackTracker(event, source, weapon); + } + } + + public static @Nullable SpongeAttackTracker of(final DamageSource source) { + return source.getDirectEntity() instanceof TrackedAttackBridge tracked ? tracked.attack$tracker() : null; + } +} diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageModifier.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageModifier.java new file mode 100644 index 00000000000..e5c90842f13 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageModifier.java @@ -0,0 +1,81 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.cause.entity.damage; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.cause.entity.damage.DamageModifier; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; + +public record SpongeDamageModifier(DamageStepType type, Optional> frameModifier, Optional damageFunction) implements DamageModifier { + + public static class Builder implements DamageModifier.Builder { + private @Nullable DamageStepType type; + private @Nullable Consumer frame; + private Function function; + + public Builder() { + this.reset(); + } + + @Override + public DamageModifier.Builder reset() { + this.type = null; + this.frame = null; + this.function = (step, damage) -> damage; + return this; + } + + @Override + public DamageModifier.Builder type(DamageStepType type) { + this.type = Objects.requireNonNull(type, "type"); + return this; + } + + @Override + public DamageModifier.Builder frameModifier(Consumer frame) { + this.frame = Objects.requireNonNull(frame, "frame"); + return this; + } + + @Override + public DamageModifier.Builder damageFunction(Function function) { + this.function = Objects.requireNonNull(function, "function"); + return this; + } + + @Override + public DamageModifier build() { + if (this.type == null) { + throw new IllegalStateException("type must be set"); + } + return new SpongeDamageModifier(this.type, Optional.ofNullable(this.frame), Optional.ofNullable(this.function)); + } + } +} diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageSourceBuilder.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageSourceBuilder.java index 6e3b1bb45ce..6cbc695fee1 100644 --- a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageSourceBuilder.java +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageSourceBuilder.java @@ -63,7 +63,7 @@ public DamageSource build() throws IllegalStateException { @Override public DamageSource.Builder type(final DamageType damageType) { - final var registry = SpongeCommon.server().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE); + final var registry = SpongeCommon.vanillaRegistry(Registries.DAMAGE_TYPE); this.damageType = registry.wrapAsHolder((net.minecraft.world.damagesource.DamageType) (Object) damageType); return this; } diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageSources.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageSources.java index 799529074ef..c989267b859 100644 --- a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageSources.java +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageSources.java @@ -24,7 +24,10 @@ */ package org.spongepowered.common.event.cause.entity.damage; +import net.minecraft.core.BlockPos; import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; +import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.api.world.server.ServerWorld; public final class SpongeDamageSources { @@ -42,6 +45,16 @@ public final class SpongeDamageSources { IGNORED = null; } + public static net.minecraft.world.damagesource.DamageSource createBlockBasedDamageSource( + final ServerWorld world, final BlockPos pos, final net.minecraft.world.damagesource.DamageSource original) { + final ServerLocation location = ServerLocation.of(world, pos.getX(), pos.getY(), pos.getZ()); + return (net.minecraft.world.damagesource.DamageSource) DamageSource.builder() + .from((DamageSource) original) + .block(location) + .block(location.createSnapshot()) + .build(); + } + // SORTFIELDS:OFF // @formatter:on diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageStep.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageStep.java new file mode 100644 index 00000000000..a3564a43653 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageStep.java @@ -0,0 +1,261 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.cause.entity.damage; + +import com.google.common.collect.ImmutableList; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.cause.entity.damage.DamageModifier; +import org.spongepowered.api.event.cause.entity.damage.DamageStep; +import org.spongepowered.api.event.cause.entity.damage.DamageStepHistory; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; +import org.spongepowered.common.event.tracking.PhaseTracker; + +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.Set; +import java.util.StringJoiner; +import java.util.function.Consumer; + +public final class SpongeDamageStep implements DamageStep { + private static final Logger LOGGER = LogManager.getLogger(); + + private final SpongeDamageTracker tracker; + private final DamageStepType type; + private final @Nullable SpongeDamageStep parent; + private final @Nullable Consumer frameModifier; + private final DamageModifier.@Nullable Function damageFunction; + + private Cause cause; + private boolean skipped; + + private OptionalDouble damageBeforeChildren = OptionalDouble.empty(); + private OptionalDouble damageBeforeSelf = OptionalDouble.empty(); + private OptionalDouble damageAfterSelf = OptionalDouble.empty(); + private OptionalDouble damageAfterChildren = OptionalDouble.empty(); + + private List childrenBefore; + private List childrenAfter; + + public SpongeDamageStep(final SpongeDamageTracker tracker, final DamageStepType type, final Object[] causes) { + this.tracker = tracker; + this.parent = null; + this.type = type; + this.frameModifier = (frame) -> { + SpongeDamageTracker.generateCauseFor(tracker.source, frame); + for (Object cause : causes) { + frame.pushCause(cause); + } + }; + this.damageFunction = null; + } + + public SpongeDamageStep(final SpongeDamageStep parent, final DamageModifier modifier) { + this.tracker = parent.tracker; + this.parent = parent; + this.type = modifier.type(); + this.frameModifier = modifier.frameModifier().orElse(null); + this.damageFunction = modifier.damageFunction().orElse(null); + } + + void populateChildren() { + this.populateChildren(new HashSet<>()); + } + + private void populateChildren(final Set parentTypes) { + parentTypes.add(this.type); + + final ImmutableList.Builder before = ImmutableList.builder(); + for (final DamageModifier modifier : this.tracker.preEvent().modifiersBefore(this.type)) { + if (parentTypes.contains(modifier.type())) { + LOGGER.warn("Modifier {} is supposed to be a child before step {} but this would cause a cycle so it will be ignored.", modifier, this); + } else { + before.add(new SpongeDamageStep(this, modifier)); + } + } + this.childrenBefore = before.build(); + + final ImmutableList.Builder after = ImmutableList.builder(); + for (final DamageModifier modifier : this.tracker.preEvent().modifiersAfter(this.type)) { + if (parentTypes.contains(modifier.type())) { + LOGGER.warn("Modifier {} is supposed to be a child after step {} but this would cause a cycle so it will be ignored.", modifier, this); + } else { + after.add(new SpongeDamageStep(this, modifier)); + } + } + this.childrenAfter = after.build(); + + final PhaseTracker phaseTracker = PhaseTracker.getInstance(); + try (final CauseStackManager.StackFrame frame = this.frameModifier == null ? null : phaseTracker.pushCauseFrame()) { + if (frame != null) { + try { + this.frameModifier.accept(frame); + } catch (final Throwable t) { + LOGGER.error("Failed to apply frame modifier of step {}", this, t); + } + } + + this.cause = phaseTracker.currentCause(); + + for (final SpongeDamageStep child : this.childrenBefore) { + child.populateChildren(parentTypes); + } + for (final SpongeDamageStep child : this.childrenAfter) { + child.populateChildren(parentTypes); + } + } + + parentTypes.remove(this.type); + } + + @Override + public DamageStepType type() { + return this.type; + } + + @Override + public Cause cause() { + return this.cause; + } + + @Override + public boolean isSkipped() { + return this.skipped; + } + + @Override + public void setSkipped(boolean skipped) { + if (this.damageAfterSelf.isPresent()) { + throw new IllegalStateException("Step can only be skipped before occurring"); + } + this.skipped = skipped; + } + + @Override + public OptionalDouble damageBeforeChildren() { + return this.damageBeforeChildren; + } + + @Override + public OptionalDouble damageBeforeSelf() { + return this.damageBeforeSelf; + } + + @Override + public OptionalDouble damageAfterSelf() { + return this.damageAfterSelf; + } + + @Override + public OptionalDouble damageAfterChildren() { + return this.damageAfterChildren; + } + + @Override + public DamageStepHistory history() { + return this.tracker; + } + + @Override + public Optional parent() { + return Optional.ofNullable(this.parent); + } + + @Override + public List childrenBefore() { + return (List) this.childrenBefore; + } + + @Override + public List childrenAfter() { + return (List) this.childrenAfter; + } + + @Override + public String toString() { + // don't add the parent to avoid infinite recursion + return new StringJoiner(", ", SpongeDamageStep.class.getSimpleName() + "[", "]") + .add("type=" + this.type) + .add("cause=" + this.cause) + .add("frameModifier=" + this.frameModifier) + .add("damageFunction=" + this.damageFunction) + .add("skipped=" + this.skipped) + .add("damageBeforeChildren=" + this.damageBeforeChildren) + .add("damageBeforeSelf=" + this.damageBeforeSelf) + .add("damageAfterSelf=" + this.damageAfterSelf) + .add("damageAfterChildren=" + this.damageAfterChildren) + .add("childrenBefore=" + this.childrenBefore) + .add("childrenAfter=" + this.childrenAfter) + .toString(); + } + + public double applyChildrenBefore(double damage) { + if (this.damageBeforeChildren.isPresent()) { + throw new IllegalStateException(); + } + + this.damageBeforeChildren = OptionalDouble.of(damage); + for (final SpongeDamageStep child : this.childrenBefore) { + damage = child.apply(damage); + } + this.damageBeforeSelf = OptionalDouble.of(damage); + return damage; + } + + public double applyChildrenAfter(double damage) { + if (this.damageAfterSelf.isPresent() || this.damageBeforeSelf.isEmpty()) { + throw new IllegalStateException(); + } + + if (this.skipped) { + damage = this.damageBeforeSelf.getAsDouble(); + } + + this.damageAfterSelf = OptionalDouble.of(damage); + for (final SpongeDamageStep child : this.childrenAfter) { + damage = child.apply(damage); + } + this.damageAfterChildren = OptionalDouble.of(damage); + return damage; + } + + public double apply(double damage) { + damage = this.applyChildrenBefore(damage); + if (!this.skipped && this.damageFunction != null) { + try { + damage = this.damageFunction.modify(this, damage); + } catch (final Throwable t) { + LOGGER.error("Failed to apply damage function of step {}", this, t); + } + } + damage = this.applyChildrenAfter(damage); + return damage; + } +} diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageModifierType.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageStepType.java similarity index 82% rename from src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageModifierType.java rename to src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageStepType.java index f17035514b7..959438dabd1 100644 --- a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageModifierType.java +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageStepType.java @@ -25,16 +25,15 @@ package org.spongepowered.common.event.cause.entity.damage; import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierType; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; import org.spongepowered.api.registry.RegistryTypes; -public final class SpongeDamageModifierType implements DamageModifierType { +public final class SpongeDamageStepType implements DamageStepType { @Override public String toString() { - return RegistryTypes.DAMAGE_MODIFIER_TYPE.get().findValueKey(this) + return RegistryTypes.DAMAGE_STEP_TYPE.get().findValueKey(this) .map(ResourceKey::toString) - .map("DamageModifierType[%s]"::formatted) - .orElse(super.toString()); + .orElseGet(super::toString); } } diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTracker.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTracker.java new file mode 100644 index 00000000000..0e5b27dfb5c --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTracker.java @@ -0,0 +1,214 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.cause.entity.damage; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.player.Player; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.cause.entity.damage.DamageStep; +import org.spongepowered.api.event.cause.entity.damage.DamageStepHistory; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; +import org.spongepowered.api.event.entity.DamageCalculationEvent; +import org.spongepowered.api.event.entity.DamageEntityEvent; +import org.spongepowered.api.registry.DefaultedRegistryReference; +import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.bridge.CreatorTrackedBridge; +import org.spongepowered.common.bridge.world.damagesource.DamageSourceBridge; +import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.util.VecHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SpongeDamageTracker implements DamageStepHistory { + private static final Logger LOGGER = LogManager.getLogger(); + + private final List rootSteps = new ArrayList<>(); + protected final DamageCalculationEvent.Pre preEvent; + protected final DamageSource source; + protected DamageCalculationEvent.Post postEvent; + + public SpongeDamageTracker(final DamageCalculationEvent.Pre preEvent, final DamageSource source) { + this.preEvent = preEvent; + this.source = source; + } + + @Override + public List rootSteps() { + return Collections.unmodifiableList(this.rootSteps); + } + + public DamageCalculationEvent.Pre preEvent() { + return this.preEvent; + } + + public DamageCalculationEvent.Post postEvent() { + if (this.postEvent == null) { + throw new IllegalStateException("Post event not yet fired"); + } + return this.postEvent; + } + + public SpongeDamageStep newStep(final DefaultedRegistryReference typeRef, final Object... causes) { + final DamageStepType type = typeRef.get(); + final SpongeDamageStep step = new SpongeDamageStep(this, type, causes); + step.populateChildren(); + + if (this.postEvent != null) { + LOGGER.warn("A new root step {} is being captured after the post event.", step.type()); + } + + if (!this.rootSteps.isEmpty()) { + final SpongeDamageStep previous = this.rootSteps.getLast(); + if (previous.damageAfterChildren().isEmpty()) { + LOGGER.warn("A new root step {} is being captured but previous root step {} hasn't finished.", step.type(), previous.type()); + this.rootSteps.removeLast(); + } + } + + this.rootSteps.add(step); + return step; + } + + public float startStep(final DefaultedRegistryReference typeRef, final float damage, final Object... causes) { + return (float) this.newStep(typeRef, causes).applyChildrenBefore(damage); + } + + public @Nullable SpongeDamageStep currentStep(final DefaultedRegistryReference typeRef) { + if (this.rootSteps.isEmpty()) { + LOGGER.warn("Expected a current root step of type {} but no step has been captured yet.", typeRef.location()); + return null; + } + + final DamageStepType type = typeRef.get(); + final SpongeDamageStep step = this.rootSteps.getLast(); + if (step.type() != type) { + LOGGER.warn("Expected a current root step of type {} but got {}.", type, step.type()); + return null; + } + return step; + } + + public float endStep(final DefaultedRegistryReference typeRef, final float damage) { + final SpongeDamageStep step = this.currentStep(typeRef); + return step == null ? damage : (float) step.applyChildrenAfter(damage); + } + + public boolean isSkipped(final DefaultedRegistryReference typeRef) { + final SpongeDamageStep step = this.currentStep(typeRef); + return step != null && step.isSkipped(); + } + + public @Nullable SpongeDamageStep lastStep(final DefaultedRegistryReference typeRef) { + final DamageStepType type = typeRef.get(); + for (int i = this.rootSteps.size() - 1; i >= 0; i--) { + final SpongeDamageStep step = this.rootSteps.get(i); + if (step.type() == type) { + return step; + } + } + return null; + } + + public float damageAfter(final DefaultedRegistryReference typeRef) { + final SpongeDamageStep step = this.lastStep(typeRef); + return step == null ? 0 : (float) step.damageAfterChildren().orElse(0); + } + + public float applyStartStep() { + return (float) this.newStep(DamageStepTypes.START).apply(this.preEvent.baseDamage()); + } + + public float callDamagePostEvent(final Entity entity, float finalDamage) { + if (this.postEvent != null) { + throw new IllegalStateException("Post event already fired"); + } + + finalDamage = (float) this.newStep(DamageStepTypes.END).apply(finalDamage); + + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + SpongeDamageTracker.generateCauseFor((DamageSource) this.preEvent.source(), frame); + + final DamageEntityEvent.Post event = SpongeEventFactory.createDamageEntityEventPost(frame.currentCause(), + entity, this, this.preEvent.baseDamage(), finalDamage); + + this.postEvent = event; + if (SpongeCommon.post(event)) { + return 0; + } + return (float) event.finalDamage(); + } + } + + protected static void generateCauseFor(final DamageSource source, final CauseStackManager.StackFrame frame) { + if (source.getDirectEntity() instanceof org.spongepowered.api.entity.Entity entity) { + if (!(entity instanceof Player) && entity instanceof CreatorTrackedBridge creatorBridge) { + creatorBridge.tracker$getCreatorUUID().ifPresent(creator -> frame.addContext(EventContextKeys.CREATOR, creator)); + creatorBridge.tracker$getNotifierUUID().ifPresent(notifier -> frame.addContext(EventContextKeys.NOTIFIER, notifier)); + } + } else if (((DamageSourceBridge) source).bridge$blockLocation() != null) { + final ServerLocation location = ((DamageSourceBridge) source).bridge$blockLocation(); + final BlockPos blockPos = VecHelper.toBlockPos(location); + final LevelChunkBridge chunkBridge = (LevelChunkBridge) ((net.minecraft.world.level.Level) location.world()).getChunkAt(blockPos); + chunkBridge.bridge$getBlockCreatorUUID(blockPos).ifPresent(creator -> frame.addContext(EventContextKeys.CREATOR, creator)); + chunkBridge.bridge$getBlockNotifierUUID(blockPos).ifPresent(notifier -> frame.addContext(EventContextKeys.NOTIFIER, notifier)); + } + frame.pushCause(source); + } + + public static @Nullable SpongeDamageTracker callDamagePreEvent(final Entity entity, final DamageSource source, final float baseDamage) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + SpongeDamageTracker.generateCauseFor(source, frame); + + final DamageEntityEvent.Pre event = SpongeEventFactory.createDamageEntityEventPre(frame.currentCause(), entity, baseDamage); + if (SpongeCommon.post(event)) { + return null; + } + + return new SpongeDamageTracker(event, source); + } + } + + public static DamageEntityEvent.@Nullable Post callDamageEvents(final Entity entity, final DamageSource source, final float baseDamage) { + final @Nullable SpongeDamageTracker tracker = SpongeDamageTracker.callDamagePreEvent(entity, source, baseDamage); + if (tracker == null) { + return null; + } + final float finalDamage = tracker.applyStartStep(); + tracker.callDamagePostEvent(entity, finalDamage); + return (DamageEntityEvent.Post) tracker.postEvent; + } +} diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTypeBuilder.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTypeBuilder.java new file mode 100644 index 00000000000..ac20dcf5d56 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTypeBuilder.java @@ -0,0 +1,99 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.cause.entity.damage; + +import net.minecraft.world.damagesource.DamageEffects; +import net.minecraft.world.damagesource.DeathMessageType; +import org.spongepowered.api.event.cause.entity.damage.DamageEffect; +import org.spongepowered.api.event.cause.entity.damage.DamageScaling; +import org.spongepowered.api.event.cause.entity.damage.DamageType; + +import java.util.Objects; + +public final class SpongeDamageTypeBuilder implements DamageType.Builder { + + private String msgId; + private net.minecraft.world.damagesource.DamageScaling scaling; + private float exhaustion; + private DamageEffects effects; + private DeathMessageType deathMessageType; + + public SpongeDamageTypeBuilder() { + this.reset(); + } + + @Override + public DamageType.Builder from(final DamageType value) { + var mcValue = (net.minecraft.world.damagesource.DamageType) (Object) value; + this.msgId = mcValue.msgId(); + this.exhaustion = mcValue.exhaustion(); + this.scaling = mcValue.scaling(); + this.effects = mcValue.effects(); + this.deathMessageType = mcValue.deathMessageType(); + return this; + } + + @Override + public DamageType.Builder name(final String name) { + this.msgId = name; + return this; + } + + @Override + public DamageType.Builder scaling(final DamageScaling scaling) { + this.scaling = (net.minecraft.world.damagesource.DamageScaling) (Object) scaling; + return this; + } + + @Override + public DamageType.Builder exhaustion(final double exhaustion) { + this.exhaustion = (float) exhaustion; + return this; + } + + @Override + public DamageType.Builder effect(final DamageEffect effect) { + this.effects = ((DamageEffects) (Object) effect); + return this; + } + + @Override + public DamageType.Builder reset() { + this.msgId = null; + this.scaling = net.minecraft.world.damagesource.DamageScaling.WHEN_CAUSED_BY_LIVING_NON_PLAYER; + this.effects = DamageEffects.HURT; + this.deathMessageType = DeathMessageType.DEFAULT; + return this; + } + + @Override + public DamageType build() { + Objects.requireNonNull(this.msgId, "name"); + Objects.requireNonNull(this.scaling, "scaling"); + Objects.requireNonNull(this.effects, "effects"); + Objects.requireNonNull(this.deathMessageType, "deathMessageType"); + return (DamageType) (Object) new net.minecraft.world.damagesource.DamageType(this.msgId, this.scaling, this.exhaustion, this.effects, this.deathMessageType); + } +} diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTypeTemplate.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTypeTemplate.java deleted file mode 100644 index 32a6840a18d..00000000000 --- a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTypeTemplate.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.event.cause.entity.damage; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.RegistryOps; -import net.minecraft.world.damagesource.DamageEffects; -import net.minecraft.world.damagesource.DamageType; -import net.minecraft.world.damagesource.DeathMessageType; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.event.cause.entity.damage.DamageEffect; -import org.spongepowered.api.event.cause.entity.damage.DamageScaling; -import org.spongepowered.api.event.cause.entity.damage.DamageTypeTemplate; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.Objects; -import java.util.function.Function; - -public record SpongeDamageTypeTemplate(ResourceKey key, DamageType representedType, DataPack pack) implements DamageTypeTemplate { - - @Override - public org.spongepowered.api.event.cause.entity.damage.DamageType type() { - return (org.spongepowered.api.event.cause.entity.damage.DamageType) (Object) this.representedType; - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeDamageTypeTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized DamageType:\n" + serialized, e); - } - } - - public static JsonElement encode(final DamageTypeTemplate template, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return DamageType.DIRECT_CODEC.encodeStart(ops, (DamageType) (Object) template.type()).getOrThrow(); - } - - public static DamageType decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return DamageType.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static DamageTypeTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, - final RegistryAccess registryAccess) { - final DamageType parsed = SpongeDamageTypeTemplate.decode(packEntry, registryAccess); - return new SpongeDamageTypeTemplate(key, parsed, pack); - } - - public static final class BuilderImpl - extends AbstractDataPackEntryBuilder - implements Builder { - - private String msgId; - private net.minecraft.world.damagesource.DamageScaling scaling; - private float exhaustion; - private DamageEffects effects; - private DeathMessageType deathMessageType; - - public BuilderImpl() { - this.reset(); - } - - @Override - public Builder fromValue(final org.spongepowered.api.event.cause.entity.damage.DamageType value) { - var mcValue = (DamageType) (Object) value; - this.msgId = mcValue.msgId(); - this.exhaustion = mcValue.exhaustion(); - this.scaling = mcValue.scaling(); - this.effects = mcValue.effects(); - this.deathMessageType = mcValue.deathMessageType(); - return this; - } - - @Override - public Builder name(final String name) { - this.msgId = name; - return this; - } - - @Override - public Builder scaling(final DamageScaling scaling) { - this.scaling = (net.minecraft.world.damagesource.DamageScaling) (Object) scaling; - return this; - } - - @Override - public Builder exhaustion(final double exhaustion) { - this.exhaustion = (float) exhaustion; - return this; - } - - @Override - public Builder effect(final DamageEffect effect) { - this.effects = ((DamageEffects) (Object) effect); - return this; - } - - @Override - public Function valueExtractor() { - return DamageTypeTemplate::type; - } - - @Override - public Builder reset() { - this.key = null; - this.pack = DataPacks.DAMAGE_TYPE; - this.msgId = null; - this.scaling = net.minecraft.world.damagesource.DamageScaling.WHEN_CAUSED_BY_LIVING_NON_PLAYER; - this.effects = DamageEffects.HURT; - this.deathMessageType = DeathMessageType.DEFAULT; - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - final DamageType damageType = SpongeDamageTypeTemplate.decode(json, SpongeCommon.server().registryAccess()); - this.fromValue((org.spongepowered.api.event.cause.entity.damage.DamageType) (Object) damageType); - return this; - } - - @Override - protected DamageTypeTemplate build0() { - Objects.requireNonNull(this.msgId, "name"); - Objects.requireNonNull(this.scaling, "scaling"); - Objects.requireNonNull(this.effects, "effects"); - Objects.requireNonNull(this.deathMessageType, "deathMessageType"); - final DamageType damageType = new DamageType(this.msgId, this.scaling, this.exhaustion, this.effects, this.deathMessageType); - return new SpongeDamageTypeTemplate(this.key, damageType, this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/event/inventory/InventoryEventFactory.java b/src/main/java/org/spongepowered/common/event/inventory/InventoryEventFactory.java index 74cecb7d6ab..4808d06264a 100644 --- a/src/main/java/org/spongepowered/common/event/inventory/InventoryEventFactory.java +++ b/src/main/java/org/spongepowered/common/event/inventory/InventoryEventFactory.java @@ -25,15 +25,16 @@ package org.spongepowered.common.event.inventory; import net.kyori.adventure.text.Component; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.MenuProvider; import net.minecraft.world.SimpleMenuProvider; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.animal.horse.AbstractHorse; +import net.minecraft.world.entity.animal.equine.AbstractHorse; import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.npc.Villager; -import net.minecraft.world.entity.npc.WanderingTrader; +import net.minecraft.world.entity.npc.villager.Villager; +import net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AnvilMenu; @@ -91,9 +92,10 @@ public class InventoryEventFactory { public static boolean callPlayerInventoryPickupEvent(final Player player, final ItemEntity itemToPickup) { final ItemStack stack = itemToPickup.getItem(); final ItemStackSnapshot snapshot = ItemStackUtil.snapshotOf(stack); + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) player.level()); final ChangeInventoryEvent.Pickup.Pre event = SpongeEventFactory.createChangeInventoryEventPickupPre( - PhaseTracker.getInstance().currentCause(), + phaseTracker.currentCause(), Optional.empty(), Collections.singletonList(snapshot), ((Inventory) player.containerMenu), (Item) itemToPickup, snapshot); SpongeCommon.post(event); if (event.isCancelled()) { @@ -106,7 +108,7 @@ public static boolean callPlayerInventoryPickupEvent(final Player player, final return true; } - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = phaseTracker.getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); try (final EffectTransactor ignored = transactor.logPlayerInventoryChangeWithEffect(player, PlayerInventoryTransaction.EventCreator.PICKUP)) { for (final ItemStackSnapshot item : list) { @@ -126,7 +128,7 @@ public static boolean callPlayerInventoryPickupEvent(final Player player, final stack.setCount(0); return true; } else { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = phaseTracker.getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); final boolean added; try (final EffectTransactor ignored = transactor.logPlayerInventoryChangeWithEffect(player, PlayerInventoryTransaction.EventCreator.PICKUP)) { @@ -141,7 +143,8 @@ public static boolean callPlayerInventoryPickupEvent(final Player player, final } public static ItemStack callHopperInventoryPickupEvent(final Container inventory, final ItemEntity item, final ItemStack stack) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) item.level()); + try (final CauseStackManager.StackFrame frame = phaseTracker.pushCauseFrame()) { frame.pushCause(inventory); final ItemStackSnapshot snapshot = ItemStackUtil.snapshotOf(stack); @@ -228,7 +231,7 @@ public static boolean callInteractContainerOpenEvent(final ServerPlayer player, final ItemStackSnapshot newCursor = ItemStackUtil.snapshotOf(menu.getCarried()); final Transaction cursorTransaction = new Transaction<>(ItemStackSnapshot.empty(), newCursor); final InteractContainerEvent.Open event = - SpongeEventFactory.createInteractContainerEventOpen(PhaseTracker.getInstance().currentCause(), + SpongeEventFactory.createInteractContainerEventOpen(PhaseTracker.getWorldInstance(player.level()).currentCause(), (org.spongepowered.api.item.inventory.Container) menu, cursorTransaction); SpongeCommon.post(event); if (event.isCancelled()) { @@ -258,7 +261,7 @@ public static boolean callInteractContainerOpenEvent(final ServerPlayer player, int level = 0; if (merchant instanceof Villager villager) { display = villager.getDisplayName(); - level = villager.getVillagerData().getLevel(); + level = villager.getVillagerData().level(); } else if (merchant instanceof WanderingTrader trader) { display = trader.getDisplayName(); level = 1; diff --git a/src/main/java/org/spongepowered/common/event/lifecycle/AbstractProvideServiceEventImpl.java b/src/main/java/org/spongepowered/common/event/lifecycle/AbstractProvideServiceEventImpl.java index 6e159830d49..65428efd365 100644 --- a/src/main/java/org/spongepowered/common/event/lifecycle/AbstractProvideServiceEventImpl.java +++ b/src/main/java/org/spongepowered/common/event/lifecycle/AbstractProvideServiceEventImpl.java @@ -73,27 +73,27 @@ public String toString() { } } - public static final class EngineScopedImpl extends AbstractProvideServiceEventImpl implements ProvideServiceEvent.EngineScoped{ + public static final class EngineScopedImpl extends AbstractProvideServiceEventImpl implements ProvideServiceEvent.EngineScoped{ - private final Engine engine; + private final TypeToken engineToken; - public EngineScopedImpl(final Cause cause, final Game game, final TypeToken token, final Engine engine) { + public EngineScopedImpl(final Cause cause, final Game game, final TypeToken token, final TypeToken engineToken) { super(cause, game, token); - this.engine = engine; + this.engineToken = engineToken; } @Override - public Engine engine() { - return this.engine; + public TypeToken secondaryParamType() { + return this.engineToken; } @Override public String toString() { return new StringJoiner(", ", EngineScopedImpl.class.getSimpleName() + "[", "]") - .add("cause=" + this.cause) - .add("token=" + this.token) - .add("engine=" + this.token) - .toString(); + .add("cause=" + this.cause) + .add("token=" + this.token) + .add("engine=" + this.token) + .toString(); } } } diff --git a/src/main/java/org/spongepowered/common/event/lifecycle/AbstractRegisterRegistryEvent.java b/src/main/java/org/spongepowered/common/event/lifecycle/AbstractRegisterRegistryEvent.java index 2bdae7ef9cc..8b79eaa9d5b 100644 --- a/src/main/java/org/spongepowered/common/event/lifecycle/AbstractRegisterRegistryEvent.java +++ b/src/main/java/org/spongepowered/common/event/lifecycle/AbstractRegisterRegistryEvent.java @@ -25,19 +25,25 @@ package org.spongepowered.common.event.lifecycle; import io.leangen.geantyref.TypeToken; +import org.spongepowered.api.Client; import org.spongepowered.api.Engine; import org.spongepowered.api.Game; import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Server; import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.lifecycle.RegisterRegistryEvent; import org.spongepowered.api.registry.DuplicateRegistrationException; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.registry.RegistryRoots; import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.registry.RegistryLoader; import org.spongepowered.common.registry.SpongeRegistryHolder; import java.util.Map; import java.util.Objects; +import java.util.function.Function; import java.util.function.Supplier; public abstract class AbstractRegisterRegistryEvent extends AbstractLifecycleEvent implements RegisterRegistryEvent { @@ -64,10 +70,33 @@ public RegistryType register(final ResourceKey key, final boolean isDynam final SpongeRegistryHolder holder = this.getHolder(); final RegistryType type = RegistryType.of(RegistryRoots.SPONGE, key); - holder.createRegistry(type, defaultValues, isDynamic); + holder.createRegistry(type, () -> this.wrapWithScopedHolder(type, holder, defaultValues), isDynamic); return type; } + @Override + public RegistryType register(final ResourceKey key, final boolean isDynamic, + final Function> defaultValues, final RegistryType... dependencies) throws DuplicateRegistrationException { + Objects.requireNonNull(key, "key"); + Objects.requireNonNull(defaultValues, "defaultValues"); + Objects.requireNonNull(dependencies, "dependencies"); + + final SpongeRegistryHolder holder = this.getHolder(); + final RegistryType type = RegistryType.of(RegistryRoots.SPONGE, key); + holder.createRegistry(type, actualHolder -> this.wrapWithScopedHolder(type, holder, () -> defaultValues.apply(actualHolder)), isDynamic, dependencies); + return type; + } + + private Map wrapWithScopedHolder(final RegistryType registry, final SpongeRegistryHolder holder, final Supplier> defaultValues) { + return PhaseTracker.getInstance().switchStartupRegistryHolder( + holder, defaultValues, + (exception) -> { + // If we don't catch it, vanilla will shut the server with "Failed to load datapacks" + SpongeCommon.logger().error("Could not supply default values for registry {}", registry.toString(), exception); + return Map.of(); + }); + } + protected abstract SpongeRegistryHolder getHolder(); public static final class GameScopedImpl extends AbstractRegisterRegistryEvent implements RegisterRegistryEvent.GameScoped { @@ -85,13 +114,12 @@ protected SpongeRegistryHolder getHolder() { public static final class EngineScopedImpl extends AbstractRegisterRegistryEvent implements RegisterRegistryEvent.EngineScoped { private final TypeToken token; - private final E engine; + private final RegistryHolder registryHolder; - public EngineScopedImpl(final Cause cause, final Game game, final E engine) { + public EngineScopedImpl(final Cause cause, final Game game, final RegistryHolder registryHolder, final TypeToken token) { super(cause, game); - - this.token = TypeToken.get((Class) engine.getClass()); - this.engine = engine; + this.token = token; + this.registryHolder = registryHolder; } @Override @@ -101,7 +129,15 @@ public TypeToken paramType() { @Override protected SpongeRegistryHolder getHolder() { - return (SpongeRegistryHolder) this.engine; + return (SpongeRegistryHolder) this.registryHolder; + } + + public static EngineScopedImpl server(final Cause cause, final Game game, final RegistryHolder registryHolder) { + return new EngineScopedImpl<>(cause, game, registryHolder, TypeToken.get(Server.class)); + } + + public static EngineScopedImpl client(final Cause cause, final Game game, final RegistryHolder registryHolder) { + return new EngineScopedImpl<>(cause, game, registryHolder, TypeToken.get(Client.class)); } } diff --git a/src/main/java/org/spongepowered/common/event/lifecycle/AbstractRegisterRegistryValueEvent.java b/src/main/java/org/spongepowered/common/event/lifecycle/AbstractRegisterRegistryValueEvent.java index 901e6ce2650..114d9260697 100644 --- a/src/main/java/org/spongepowered/common/event/lifecycle/AbstractRegisterRegistryValueEvent.java +++ b/src/main/java/org/spongepowered/common/event/lifecycle/AbstractRegisterRegistryValueEvent.java @@ -25,37 +25,48 @@ package org.spongepowered.common.event.lifecycle; import io.leangen.geantyref.TypeToken; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.Client; import org.spongepowered.api.Engine; import org.spongepowered.api.Game; import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Server; import org.spongepowered.api.Sponge; import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; -import org.spongepowered.api.registry.DefaultedRegistryValue; import org.spongepowered.api.registry.Registry; import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.common.bridge.core.WritableRegistryBridge; +import org.spongepowered.common.registry.SpongeRegistryHolder; -import java.util.Objects; -import java.util.Optional; +import java.util.Map; +import java.util.function.BiConsumer; public abstract class AbstractRegisterRegistryValueEvent extends AbstractLifecycleEvent implements RegisterRegistryValueEvent { - public AbstractRegisterRegistryValueEvent(final Cause cause, final Game game) { + private final Map, Registry> registries; + + public AbstractRegisterRegistryValueEvent(final Cause cause, final Game game, final Map, Registry> registries) { super(cause, game); + this.registries = registries; } + @SuppressWarnings("unchecked") @Override - public RegistryStep registry(final RegistryType registryType) { - final Optional> optRegistry = this.getHolder().findRegistry(Objects.requireNonNull(registryType, "registryType")); - if (!optRegistry.isPresent()) { - throw new IllegalArgumentException(String.format("RegistryType '%s' has no registered registry!", registryType)); + public void registry(final RegistryType registryType, final BiConsumer> consumer) { + final @Nullable Registry registry = (Registry) this.registries.get(registryType); + if (registry != null) { + consumer.accept(this.getHolder(), new RegistryStepImpl<>(registry)); } - final Registry registry = optRegistry.get(); - if (!registry.isDynamic()) { - throw new IllegalStateException(String.format("RegistryType '%s' is read only and cannot accept new registry values!", registryType)); + } + + @Override + public void registry(final RegistryType registryType, final BiConsumer> consumer, final RegistryType... dependencies) { + final @Nullable Registry registry = (Registry) this.registries.get(registryType); + if (registry != null) { + ((WritableRegistryBridge) registry).bridge$addDependencies(() -> consumer.accept(this.getHolder(), new RegistryStepImpl<>(registry)), dependencies); } - return new RegistryStepImpl<>(registry); } protected abstract RegistryHolder getHolder(); @@ -75,24 +86,10 @@ public RegistryStep register(final ResourceKey key, final T value) { } } - public static final class BuiltInImpl extends AbstractLifecycleEvent implements RegisterRegistryValueEvent.BuiltIn { - - private Registry registry; - - public BuiltInImpl(final Cause cause, final Game game, Registry registry) { - super(cause, game); - this.registry = registry; - } - - @Override - public RegistryStep registry(final RegistryType registryType) { - return new RegistryStepImpl<>(registry); - } - } public static final class GameScopedImpl extends AbstractRegisterRegistryValueEvent implements RegisterRegistryValueEvent.GameScoped { - public GameScopedImpl(final Cause cause, final Game game) { - super(cause, game); + public GameScopedImpl(final Cause cause, final Game game, final Map, Registry> registries) { + super(cause, game, registries); } @Override @@ -104,12 +101,13 @@ protected RegistryHolder getHolder() { public static final class EngineScopedImpl extends AbstractRegisterRegistryValueEvent implements RegisterRegistryValueEvent.EngineScoped { private final TypeToken token; - private final E engine; + private final RegistryHolder registryHolder; - public EngineScopedImpl(final Cause cause, final Game game, final E engine) { - super(cause, game); - this.token = TypeToken.get((Class) engine.getClass()); - this.engine = engine; + public EngineScopedImpl(final Cause cause, final Game game, final RegistryHolder registryHolder, + final TypeToken token, final Map, Registry> registries) { + super(cause, game, registries); + this.token = token; + this.registryHolder = registryHolder; } @Override @@ -118,8 +116,16 @@ public TypeToken paramType() { } @Override - protected RegistryHolder getHolder() { - return this.engine; + protected SpongeRegistryHolder getHolder() { + return (SpongeRegistryHolder) this.registryHolder; + } + + public static EngineScopedImpl server(final Cause cause, final Game game, final RegistryHolder registryHolder, final Map, Registry> registries) { + return new EngineScopedImpl<>(cause, game, registryHolder, TypeToken.get(Server.class), registries); + } + + public static EngineScopedImpl client(final Cause cause, final Game game, final RegistryHolder registryHolder, final Map, Registry> registries) { + return new EngineScopedImpl<>(cause, game, registryHolder, TypeToken.get(Client.class), registries); } } @@ -127,8 +133,8 @@ public static final class WorldScopedImpl extends AbstractRegisterRegistryValueE private final ResourceKey worldKey; - public WorldScopedImpl(final Cause cause, final Game game, final ResourceKey worldKey) { - super(cause, game); + public WorldScopedImpl(final Cause cause, final Game game, final Map, Registry> registries, final ResourceKey worldKey) { + super(cause, game, registries); this.worldKey = worldKey; } diff --git a/src/main/java/org/spongepowered/common/event/lifecycle/FreezeRegistryEventImpl.java b/src/main/java/org/spongepowered/common/event/lifecycle/FreezeRegistryEventImpl.java new file mode 100644 index 00000000000..ff6eb36f7be --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/lifecycle/FreezeRegistryEventImpl.java @@ -0,0 +1,90 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.lifecycle; + +import io.leangen.geantyref.TypeToken; +import org.spongepowered.api.Client; +import org.spongepowered.api.Engine; +import org.spongepowered.api.Game; +import org.spongepowered.api.Server; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.lifecycle.FreezeRegistryEvent; +import org.spongepowered.api.registry.RegistryHolder; + +public abstract class FreezeRegistryEventImpl extends AbstractLifecycleEvent implements FreezeRegistryEvent { + + protected FreezeRegistryEventImpl(final Cause cause, final Game game) { + super(cause, game); + } + + public static abstract class PostImpl extends FreezeRegistryEventImpl implements Post { + + protected PostImpl(final Cause cause, final Game game) { + super(cause, game); + } + + public static final class GameImpl extends PostImpl implements GameScoped { + + public GameImpl(final Cause cause, final Game game) { + super(cause, game); + } + + @Override + public RegistryHolder holder() { + return this.game; + } + } + + public static final class EngineImpl extends PostImpl implements EngineScoped { + + private final RegistryHolder registryHolder; + private final TypeToken paramType; + + private EngineImpl(final Cause cause, final Game game, final RegistryHolder registryHolder, final TypeToken paramType) { + super(cause, game); + this.registryHolder = registryHolder; + this.paramType = paramType; + } + + @Override + public TypeToken paramType() { + return this.paramType; + } + + @Override + public RegistryHolder holder() { + return this.registryHolder; + } + + public static EngineImpl server(final Cause cause, final Game game, final RegistryHolder registryHolder) { + return new EngineImpl<>(cause, game, registryHolder, TypeToken.get(Server.class)); + } + + public static EngineImpl client(final Cause cause, final Game game, final RegistryHolder registryHolder) { + return new EngineImpl<>(cause, game, registryHolder, TypeToken.get(Client.class)); + } + } + } +} diff --git a/src/main/java/org/spongepowered/common/event/lifecycle/RegisterCommandEventImpl.java b/src/main/java/org/spongepowered/common/event/lifecycle/RegisterCommandEventImpl.java index 2a3a334fa98..cf47337122e 100644 --- a/src/main/java/org/spongepowered/common/event/lifecycle/RegisterCommandEventImpl.java +++ b/src/main/java/org/spongepowered/common/event/lifecycle/RegisterCommandEventImpl.java @@ -31,6 +31,7 @@ import org.spongepowered.api.command.registrar.CommandRegistrar; import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.plugin.PluginContainer; import java.util.Objects; @@ -38,10 +39,17 @@ public final class RegisterCommandEventImpl> extends AbstractLifecycleEvent.GenericImpl implements RegisterCommandEvent { private final R registrar; + private final RegistryHolder registryHolder; - public RegisterCommandEventImpl(final Cause cause, final Game game, final R registrar) { + public RegisterCommandEventImpl(final Cause cause, final Game game, final R registrar, final RegistryHolder registryHolder) { super(cause, game, registrar.type().handledType()); this.registrar = registrar; + this.registryHolder = registryHolder; + } + + @Override + public RegistryHolder registryHolder() { + return this.registryHolder; } @Override diff --git a/src/main/java/org/spongepowered/common/event/lifecycle/RegisterTagEventImpl.java b/src/main/java/org/spongepowered/common/event/lifecycle/RegisterTagEventImpl.java index bf33e3485fa..1f9e30ecdc9 100644 --- a/src/main/java/org/spongepowered/common/event/lifecycle/RegisterTagEventImpl.java +++ b/src/main/java/org/spongepowered/common/event/lifecycle/RegisterTagEventImpl.java @@ -26,10 +26,11 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.Game; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.lifecycle.RegisterTagEvent; import org.spongepowered.api.registry.DefaultedRegistryReference; -import org.spongepowered.api.registry.RegistryKey; +import org.spongepowered.api.registry.RegistryType; import org.spongepowered.api.tag.Tag; import org.spongepowered.common.tag.SpongePluginTag; import org.spongepowered.common.tag.SpongePluginTagModifier; @@ -45,7 +46,7 @@ public final class RegisterTagEventImpl extends AbstractLifecycleEvent implements RegisterTagEvent { - private final Map, SpongePluginTagModifier> tags = new HashMap<>(); + private final Map> tags = new HashMap<>(); public RegisterTagEventImpl(final Cause cause, final Game game) { super(cause, game); @@ -54,13 +55,13 @@ public RegisterTagEventImpl(final Cause cause, final Game game) { @SuppressWarnings("unchecked") @Override public TagStep tag(final Tag tag) { - final SpongePluginTagModifier modifier = this.tags.computeIfAbsent(tag, $ -> new SpongePluginTagModifier<>()); + final SpongePluginTagModifier modifier = this.tags.computeIfAbsent(new TagKey(tag), $ -> new SpongePluginTagModifier<>()); return new TagStepImpl<>((SpongePluginTagModifier) modifier, null); } public SpongePluginTags tags() { return new SpongePluginTags(this.tags.entrySet().stream().collect( - Collectors.groupingBy(e -> e.getKey().registry(), Collectors.toMap(e -> e.getKey().key(), Map.Entry::getValue)))); + Collectors.groupingBy(e -> e.getKey().registry().registryType(), Collectors.toMap(e -> e.getKey().tag(), Map.Entry::getValue)))); } private record TagStepImpl(SpongePluginTagModifier pluginTagModifier, @Nullable SpongePluginTagPredicate predicate) implements TagStep { @@ -84,7 +85,7 @@ public TagStep filterTags(final BiPredicate, DefaultedRegistryReferenc } @Override - public TagStep append(final RegistryKey key) { + public TagStep append(final org.spongepowered.api.registry.RegistryKey key) { this.pluginTagModifier.append(new SpongePluginTag(key.location(), false), this.predicate); return this; } @@ -113,4 +114,25 @@ public TagStep testTags(final BiPredicate, DefaultedRegistryReference< return this; } } + + private record TagKey(RegistryTypeKey registry, ResourceKey tag) + { + public TagKey(final Tag tag) + { + this(new RegistryTypeKey(tag.registry()), tag.key()); + } + } + + private record RegistryTypeKey(ResourceKey registryRoot, ResourceKey registryKey) + { + public RegistryTypeKey(RegistryType registryType) + { + this(registryType.root(), registryType.location()); + } + + public RegistryType registryType() + { + return RegistryType.of(this.registryRoot, this.registryKey); + } + } } diff --git a/src/main/java/org/spongepowered/common/event/manager/EventType.java b/src/main/java/org/spongepowered/common/event/manager/EventType.java index bdeb2eee17a..687d7cc01be 100644 --- a/src/main/java/org/spongepowered/common/event/manager/EventType.java +++ b/src/main/java/org/spongepowered/common/event/manager/EventType.java @@ -24,20 +24,21 @@ */ package org.spongepowered.common.event.manager; +import io.leangen.geantyref.TypeToken; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.event.Event; -import java.lang.reflect.Type; +import java.util.List; import java.util.Objects; public final class EventType { private final Class eventType; - private final @Nullable Type genericType; + private final @Nullable List> genericType; private int hashCode; - EventType(final Class eventType, final @Nullable Type genericType) { + EventType(final Class eventType, final @Nullable List> genericType) { this.genericType = genericType; this.eventType = eventType; } @@ -50,7 +51,7 @@ public Class getType() { return this.eventType; } - public @Nullable Type getGenericType() { + public @Nullable List> getGenericType() { return this.genericType; } diff --git a/src/main/java/org/spongepowered/common/event/manager/ListenerClassVisitor.java b/src/main/java/org/spongepowered/common/event/manager/ListenerClassVisitor.java index 00cd7e9cfd8..46b564d3116 100644 --- a/src/main/java/org/spongepowered/common/event/manager/ListenerClassVisitor.java +++ b/src/main/java/org/spongepowered/common/event/manager/ListenerClassVisitor.java @@ -135,7 +135,7 @@ public void visitLocalVariable( return; } if (signature != null) { // We only care about the first parameter - new SignatureReader(signature).accept(new ListenerSignatureVisitor(parameter)); + new SignatureReader(signature).accept(new ListenerSignatureVisitor(parameter).visitParameterType()); } else if (this.discoveredMethod.signature != null) { // In some compiled languages, the local variable signature is // not available, but we can still grab it from the method's @@ -194,8 +194,7 @@ public AnnotationVisitor visitParameterAnnotation( static final class ListenerSignatureVisitor extends SignatureVisitor { private final ListenerParameter parameter; - private boolean secondType = true; - private boolean isGeneric = false; + private @Nullable State state; public ListenerSignatureVisitor(final ListenerParameter parameter) { super(ListenerClassVisitor.ASM_VERSION); @@ -203,42 +202,51 @@ public ListenerSignatureVisitor(final ListenerParameter parameter) { } @Override - public void visitClassType(final String name) { - // The order of operations is that visitClassType gets called - // twice, once for the generic class, then a second time for - // the generic type. Ideally we don't have to deal with additional - // nested types, but if we do, well, we'll just ignore them. - - // Only accept the first and second type - if (!secondType) { - return; + public SignatureVisitor visitParameterType() { + if (this.state == null) { + this.state = State.PARSING; } - // The first type will be base type - if (this.parameter.baseType == null) { - this.parameter.baseType = Type.getType("L" + name + ";"); - return; - } - // The second type will be generic type or ignore - else if (isGeneric && this.parameter.genericType == null) { - this.parameter.genericType = Type.getType("L" + name + ";"); + return this; + } + + @Override + public void visitClassType(final String name) { + if (this.state == State.PARSING) { + if (this.parameter.baseType == null) { + this.parameter.baseType = Type.getType("L" + name + ";"); + } + } else if (this.state == State.GENERIC) { + this.parameter.genericTypes.add(new ListenerParameter.GenericType(Type.getType("L" + name + ";"))); } - // Ignore after second type - secondType = false; } @Override public void visitTypeArgument() { // And sometimes, some plugins just care about the parent type // and wildcard their bounds. - this.parameter.wildcard = true; + if (this.state == State.PARSING) { + this.parameter.genericTypes.add(new ListenerParameter.GenericType(null)); + } } @Override - public SignatureVisitor visitTypeArgument(char wildcard) { - // For treating the second type as generic - this.isGeneric = true; + public SignatureVisitor visitTypeArgument(final char wildcard) { + if (this.state == State.PARSING) { + this.state = State.GENERIC; + } return this; } + + @Override + public void visitEnd() { + this.state = this.state == State.GENERIC ? State.PARSING : State.DONE; + } + + private enum State { + PARSING, + GENERIC, + DONE + } } static final class ListenerAnnotationVisitor extends AnnotationVisitor { @@ -280,7 +288,7 @@ static final class ListenerExtractor extends AnnotationVisitor { private final DiscoveredMethod discoveredMethod; public ListenerExtractor( - final ListenerClassVisitor.DiscoveredMethod discoveredMethod + final DiscoveredMethod discoveredMethod ) { super(ListenerClassVisitor.ASM_VERSION); this.discoveredMethod = discoveredMethod; @@ -439,15 +447,15 @@ public static final class ListenerParameter { private final DiscoveredMethod method; private final Type type; private final List annotations; - public boolean wildcard = false; @MonotonicNonNull Type baseType; - @MonotonicNonNull Type genericType; + List genericTypes; @MonotonicNonNull String name; ListenerParameter(final DiscoveredMethod method, final Type type) { this.method = method; this.type = type; this.annotations = new LinkedList<>(); + this.genericTypes = new LinkedList<>(); } public Class clazz() throws ClassNotFoundException { @@ -490,15 +498,22 @@ public int hashCode() { } public java.lang.reflect.Type genericType() throws ClassNotFoundException { - if (this.genericType == null) { - if (this.wildcard) { - return TypeFactory.parameterizedClass(this.clazz(), TypeFactory.unboundWildcard()); - } + if (this.genericTypes.isEmpty()) { return TypeFactory.parameterizedClass(this.clazz()); } - final Class gClazz = this.method.classByLoader(this.genericType.getClassName()); -// final java.lang.reflect.Type generic = TypeFactory.parameterizedClass(gClazz); - return TypeFactory.parameterizedClass(this.clazz(), gClazz); + final java.lang.reflect.Type[] classes = new java.lang.reflect.Type[this.genericTypes.size()]; + for (int i = 0; i < classes.length; i++) { + final GenericType type = this.genericTypes.get(i); + if (type.type() != null) { + classes[i] = this.method.classByLoader(type.type().getClassName()); + } else { + classes[i] = TypeFactory.unboundWildcard(); + } + } + return TypeFactory.parameterizedClass(this.clazz(), classes); + } + + record GenericType(@Nullable Type type) { } } diff --git a/src/main/java/org/spongepowered/common/event/manager/SpongeEventManager.java b/src/main/java/org/spongepowered/common/event/manager/SpongeEventManager.java index 5806cd53175..4bc53dc0f0b 100644 --- a/src/main/java/org/spongepowered/common/event/manager/SpongeEventManager.java +++ b/src/main/java/org/spongepowered/common/event/manager/SpongeEventManager.java @@ -29,8 +29,10 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import io.leangen.geantyref.GenericTypeReflector; +import io.leangen.geantyref.TypeToken; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.BiGenericEvent; import org.spongepowered.api.event.Cancellable; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.Event; @@ -60,6 +62,7 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -125,14 +128,20 @@ public SpongeEventManager() { @SuppressWarnings({"unchecked", "rawtypes"}) private static RegisteredListener createRegistration(final PluginContainer plugin, final Type eventType, final Order order, final boolean beforeModifications, final EventListener handler) { - @Nullable Type genericType = null; + @Nullable List> genericType = null; final Class erased = GenericTypeReflector.erase(eventType); - if (GenericEvent.class.isAssignableFrom(erased)) { - genericType = TypeTokenUtil.typeArgumentFromSupertype(eventType, GenericEvent.class, 0); + if (BiGenericEvent.class.isAssignableFrom(erased)) { + genericType = SpongeEventManager.typeArgumentsFromSupertype(eventType, BiGenericEvent.class); + } else if (GenericEvent.class.isAssignableFrom(erased)) { + genericType = SpongeEventManager.typeArgumentsFromSupertype(eventType, GenericEvent.class); } return new RegisteredListener(plugin, new EventType(erased, genericType), order, handler, beforeModifications); } + private static List> typeArgumentsFromSupertype(final Type sub, final Class superType) { + return Arrays.stream(TypeTokenUtil.typeArgumentsFromSupertype(sub, superType)).map(TypeToken::get).toList(); + } + RegisteredListener.Cache bakeHandlers(final EventType eventType) { final List> handlers = new ArrayList<>(); final Stream> types = Types.allSuperTypesAndInterfaces(eventType.getType()) @@ -146,12 +155,20 @@ RegisteredListener.Cache bakeHandlers(final EventType event final Class type = it.next(); final Collection> listeners = this.handlersByEvent.get(type); if (GenericEvent.class.isAssignableFrom(type)) { - final Type genericType = Objects.requireNonNull(eventType.getGenericType()); - for (final RegisteredListener listener : listeners) { - final Type genericType1 = Objects.requireNonNull(listener.getEventType().getGenericType()); - if (TypeTokenUtil.isAssignable(genericType, genericType1)) { - handlers.add(listener); + final List> eventGenericTypes = Objects.requireNonNull(eventType.getGenericType()); + listeners: for (final RegisteredListener listener : listeners) { + final List> listenerGenericTypes = Objects.requireNonNull(listener.getEventType().getGenericType()); + if (eventGenericTypes.size() != listenerGenericTypes.size()) { + continue; + } + for (int i = 0; i < eventGenericTypes.size(); i++) { + final TypeToken eventGenericType = Objects.requireNonNull(eventGenericTypes.get(i)); + final TypeToken listenerGenericType = Objects.requireNonNull(listenerGenericTypes.get(i)); + if (!TypeTokenUtil.isAssignable(eventGenericType.getType(), listenerGenericType.getType())) { + continue listeners; + } } + handlers.add(listener); } } else { handlers.addAll(listeners); @@ -297,6 +314,7 @@ public EventManager registerListener(final EventListenerRegist return this; } + @SuppressWarnings("deprecation") @Override public SpongeEventManager registerListeners(final PluginContainer plugin, final Object listener) { this.registerListener(plugin, listener, null); @@ -355,8 +373,8 @@ public SpongeEventManager unregisterListeners(final Object obj) { protected RegisteredListener.Cache getHandlerCache(final Event event) { final Class eventClass = Objects.requireNonNull(event, "event").getClass(); final EventType eventType; - if (event instanceof GenericEvent) { - eventType = new EventType(eventClass, Objects.requireNonNull(((GenericEvent) event).paramType().getType())); + if (event instanceof GenericEvent genericEvent) { + eventType = new EventType(eventClass, Objects.requireNonNull(genericEvent.paramTypes())); } else { eventType = new EventType(eventClass, null); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/IPhaseState.java b/src/main/java/org/spongepowered/common/event/tracking/IPhaseState.java index 4aa6301a441..01d41d6ced4 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/IPhaseState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/IPhaseState.java @@ -58,6 +58,7 @@ import org.spongepowered.api.item.inventory.transaction.SlotTransaction; import org.spongepowered.api.world.BlockChangeFlag; import org.spongepowered.api.world.World; +import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.common.block.SpongeBlockSnapshot; import org.spongepowered.common.bridge.server.TickTaskBridge; import org.spongepowered.common.bridge.server.level.ServerLevelBridge; @@ -155,12 +156,14 @@ default boolean isInteraction() { /** * Performs any necessary custom logic after the provided {@link BlockSnapshot} * {@link Transaction} has taken place. - * @param context The context for information + * + * @param context The context for information + * @param serverWorld * @param blockChange The block change performed - * @param receipt The transaction of the old and new snapshots + * @param receipt The transaction of the old and new snapshots */ default void postBlockTransactionApplication( - final C context, final BlockChange blockChange, + final C context, ServerWorld serverWorld, final BlockChange blockChange, final BlockTransactionReceipt receipt ) { } diff --git a/src/main/java/org/spongepowered/common/event/tracking/PhaseContext.java b/src/main/java/org/spongepowered/common/event/tracking/PhaseContext.java index f35655813a3..fbd4e95aeba 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/PhaseContext.java +++ b/src/main/java/org/spongepowered/common/event/tracking/PhaseContext.java @@ -161,7 +161,7 @@ public P buildAndSwitch() { if (SpongeConfigs.getCommon().get().phaseTracker.generateStackTracePerPhase) { this.stackTrace = new Exception("Debug Trace").getStackTrace(); } - PhaseTracker.getInstance().switchToPhase(this.state, this); + this.createdTracker.switchToPhase(this.state, this); return (P) this; } @@ -322,16 +322,15 @@ public void close() { // Should never throw an exception this.state, this, new IllegalStateException("Closing empty phase context")); return; } - final PhaseTracker instance = PhaseTracker.getInstance(); - instance.completePhase(this); + this.createdTracker.completePhase(this); if (this.usedFrame != null) { - this.usedFrame.iterator().forEachRemaining(instance::popCauseFrame); + this.usedFrame.iterator().forEachRemaining(this.createdTracker::popCauseFrame); this.usedFrame = null; } else if (this.shouldProvideModifiers()) { // So, this part is interesting... Since the used frame is null, that means // the cause stack manager still has the reference of this context/phase, we have // to "pop off" the list. - instance.popFrameMutator(this); + this.createdTracker.popFrameMutator(this); } this.reset(); this.isCompleted = false; diff --git a/src/main/java/org/spongepowered/common/event/tracking/PhasePrinter.java b/src/main/java/org/spongepowered/common/event/tracking/PhasePrinter.java index 9f4458ba773..12e1eab6fd9 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/PhasePrinter.java +++ b/src/main/java/org/spongepowered/common/event/tracking/PhasePrinter.java @@ -25,7 +25,6 @@ package org.spongepowered.common.event.tracking; import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.Block; import org.apache.logging.log4j.Level; @@ -152,7 +151,7 @@ static void printExceptionSpawningEntity(final PhaseTracker tracker, final Phase } static void printNullSourceBlockWithTile( - final BlockPos pos, final Block blockIn, final BlockPos otherPos, final ResourceLocation type, final boolean useTile, + final BlockPos pos, final Block blockIn, final BlockPos otherPos, final String type, final boolean useTile, final NullPointerException e) { final PhaseTracker instance = PhaseTracker.getInstance(); final PrettyPrinter printer = new PrettyPrinter(60).add("Null Source Block on TileEntity!").centre().hr() @@ -161,7 +160,7 @@ static void printNullSourceBlockWithTile( + "attempting to perform a neighbor notification with it. Because " + "this is guaranteed to lead to a crash or a spam of reports, " + "Sponge is going ahead and fixing the issue. The offending Tile " - + "is " + type.toString()) + + "is " + type) .add() .add("%s : %s", "Source position", pos) .add("%s : %s", "Source TileEntity", type) diff --git a/src/main/java/org/spongepowered/common/event/tracking/PhaseStateProxy.java b/src/main/java/org/spongepowered/common/event/tracking/PhaseStateProxy.java index aa58d508400..752862e0267 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/PhaseStateProxy.java +++ b/src/main/java/org/spongepowered/common/event/tracking/PhaseStateProxy.java @@ -51,6 +51,7 @@ import org.spongepowered.api.item.inventory.Slot; import org.spongepowered.api.item.inventory.transaction.SlotTransaction; import org.spongepowered.api.world.BlockChangeFlag; +import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.common.block.SpongeBlockSnapshot; import org.spongepowered.common.bridge.server.TickTaskBridge; import org.spongepowered.common.bridge.world.TrackedWorldBridge; @@ -99,11 +100,13 @@ default boolean isInteraction() { /** * Performs any necessary custom logic after the provided {@link BlockSnapshot} * {@link Transaction} has taken place. - * @param blockChange The block change performed - * @param receipt The transaction of the old and new snapshots + * + * @param serverWorld + * @param blockChange The block change performed + * @param receipt The transaction of the old and new snapshots */ - default void postBlockTransactionApplication(final BlockChange blockChange, final BlockTransactionReceipt receipt) { - this.getState().postBlockTransactionApplication(this.asContext(), blockChange, receipt); + default void postBlockTransactionApplication(ServerWorld serverWorld, final BlockChange blockChange, final BlockTransactionReceipt receipt) { + this.getState().postBlockTransactionApplication(this.asContext(), serverWorld, blockChange, receipt); } /** diff --git a/src/main/java/org/spongepowered/common/event/tracking/PhaseTracker.java b/src/main/java/org/spongepowered/common/event/tracking/PhaseTracker.java index 4fb5a0c57dc..b5708d57cf6 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/PhaseTracker.java +++ b/src/main/java/org/spongepowered/common/event/tracking/PhaseTracker.java @@ -29,9 +29,9 @@ import com.google.common.collect.Maps; import com.google.common.collect.Queues; import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -48,6 +48,7 @@ import org.spongepowered.api.event.EventContext; import org.spongepowered.api.event.EventContextKey; import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.scheduler.Task; import org.spongepowered.api.util.Ticks; import org.spongepowered.common.SpongeCommon; @@ -58,6 +59,7 @@ import org.spongepowered.common.launch.Launch; import org.spongepowered.common.launch.config.common.PhaseTrackerCategory; import org.spongepowered.common.launch.config.core.SpongeConfigs; +import org.spongepowered.common.registry.SpongeRegistryHolder; import org.spongepowered.common.util.Constants; import org.spongepowered.common.util.Preconditions; import org.spongepowered.common.util.PrettyPrinter; @@ -79,6 +81,8 @@ import java.util.StringJoiner; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; +import java.util.function.Supplier; /** * The core state machine of Sponge. Acts a as proxy between various engine objects by processing actions through @@ -89,9 +93,19 @@ public final class PhaseTracker implements CauseStackManager { public static final PhaseTracker CLIENT = new PhaseTracker(); - public static final PhaseTracker SERVER = new PhaseTracker(); + private static final PhaseTracker SERVER = new PhaseTracker(); public static final Logger LOGGER = LogManager.getLogger(PhaseTracker.class); static final CopyOnWriteArrayList ASYNC_CAPTURED_ENTITIES = new CopyOnWriteArrayList<>(); + + /** + * Queue an entity that was offered to a main-thread-only entity addition API from an + * off-thread context (typically a worldgen worker invoking {@code ServerLevel#addFreshEntity} + * directly instead of going through the {@code WorldGenLevel} it was given). The per-tick + * drain task in {@link #init()} re-adds these on the main thread under a forced spawn cause. + */ + public static void captureAsyncEntity(final net.minecraft.world.entity.Entity entity) { + PhaseTracker.ASYNC_CAPTURED_ENTITIES.add(entity); + } private static final Map SPINOFF_TRACKERS = new MapMaker().weakKeys().concurrencyLevel(8).makeMap(); private static final boolean DEBUG_CAUSE_FRAMES = Boolean.parseBoolean(System.getProperty("sponge.debugcauseframes", "false")); private static final String INITIAL_POOL_SIZE_PROPERTY = "sponge.cause.initialFramePoolSize"; @@ -121,6 +135,44 @@ public static PhaseTracker createNew(final Thread thread) { } } + /** + * The server phase tracker should be used for actions + * that are not tied to any specific {@link ServerLevel}. + * + *

For actions that are specific to a {@link ServerLevel}, + * the {@link #getWorldInstance(ServerLevel)} should be + * preferred.

+ * + *

For actions that could be performed inside a world + * generation thread, the {@link #getWorldInstance(LevelAccessor)} + * should be used instead.

+ * + *

If the current world instance is unknown, the + * implementation can perform more expensive lookup + * using the parameterless {@link #getWorldInstance}.

+ */ + public static PhaseTracker getServerInstanceExplicitly() { + return PhaseTracker.SERVER; + } + + public static PhaseTracker getWorldInstance() { + //This is reserved for a mod that wishes to implement + //multithreaded levels. + return PhaseTracker.SERVER; + } + + public static PhaseTracker getWorldInstance(final LevelAccessor level) { + //This is reserved for a mod that wishes to implement + //multithreaded levels. + return PhaseTracker.SERVER; + } + + public static PhaseTracker getWorldInstance(final ServerLevel level) { + //This is reserved for a mod that wishes to implement + //multithreaded levels. + return PhaseTracker.SERVER; + } + public static Block validateBlockForNeighborNotification(final ServerLevel worldServer, final BlockPos pos, @Nullable Block blockIn, final BlockPos otherPos, final LevelChunk chunk) { if (blockIn == null) { @@ -135,11 +187,8 @@ public static Block validateBlockForNeighborNotification(final ServerLevel world final @Nullable BlockEntityType type = Optional.ofNullable(source) .map(BlockEntity::getType) .orElse(null); - if (type != null) { - @Nullable ResourceLocation id = BlockEntityType.getKey(type); - if (id == null) { - id = ResourceLocation.parse(source.getClass().getCanonicalName()); - } + if (source != null) { + String id = source.typeHolder().getRegisteredName(); final Map autoFixedTiles = trackerConfig.autoFixNullSourceBlockProvidingBlockEntities; final boolean contained = autoFixedTiles.containsKey(type.toString()); // If we didn't map the tile entity yet, we should apply the mapping @@ -151,7 +200,7 @@ public static Block validateBlockForNeighborNotification(final ServerLevel world if (useTile) { blockIn = source.getBlockState().getBlock(); } else { - blockIn = (pos.getX() >> 4 == chunk.getPos().x && pos.getZ() >> 4 == chunk.getPos().z) + blockIn = (pos.getX() >> 4 == chunk.getPos().x() && pos.getZ() >> 4 == chunk.getPos().z()) ? chunk.getBlockState(pos).getBlock() : worldServer.getBlockState(pos).getBlock(); } @@ -159,7 +208,7 @@ public static Block validateBlockForNeighborNotification(final ServerLevel world PhasePrinter.printNullSourceBlockWithTile(pos, blockIn, otherPos, id, useTile, new NullPointerException("Null Source Block For TileEntity Neighbor Notification")); } } else { - blockIn = (pos.getX() >> 4 == chunk.getPos().x && pos.getZ() >> 4 == chunk.getPos().z) + blockIn = (pos.getX() >> 4 == chunk.getPos().x() && pos.getZ() >> 4 == chunk.getPos().z()) ? chunk.getBlockState(pos).getBlock() : worldServer.getBlockState(pos).getBlock(); if (trackerConfig.reportNullSourceBlocksOnNeighborNotifications) { @@ -169,7 +218,7 @@ public static Block validateBlockForNeighborNotification(final ServerLevel world } } else { - blockIn = (pos.getX() >> 4 == chunk.getPos().x && pos.getZ() >> 4 == chunk.getPos().z) + blockIn = (pos.getX() >> 4 == chunk.getPos().x() && pos.getZ() >> 4 == chunk.getPos().z()) ? chunk.getBlockState(pos).getBlock() : worldServer.getBlockState(pos).getBlock(); if (trackerConfig.reportNullSourceBlocksOnNeighborNotifications) { @@ -222,6 +271,7 @@ public static Block validateBlockForNeighborNotification(final ServerLevel world private final Deque> phaseContextProviders = new ArrayDeque<>(); final PhaseStack stack = new PhaseStack(); private final SpongeCauseStackManager api = new SpongeCauseStackManager(); + private @Nullable SpongeRegistryHolder startupRegistryHolder; PhaseTracker() { for (int i = 0; i < PhaseTracker.INITIAL_POOL_SIZE; i++) { @@ -230,7 +280,7 @@ public static Block validateBlockForNeighborNotification(final ServerLevel world } public void init() { - if (this != PhaseTracker.SERVER) { + if (this == PhaseTracker.CLIENT) { return; } if (this.hasRun) { @@ -252,7 +302,7 @@ public void init() { frame.addContext(EventContextKeys.SPAWN_TYPE, SpongeSpawnTypes.FORCED); for (final Entity entity : entities) { // At this point, we don't care what the causes are... - entity.getCommandSenderWorld().addFreshEntity(entity); + entity.level().addFreshEntity(entity); } } @@ -301,14 +351,14 @@ public boolean onSidedThread() { // ----------------- SIMPLE GETTERS -------------------------------------- public IPhaseState getCurrentState() { - if (Thread.currentThread() != this.getSidedThread()) { + if (!this.onSidedThread()) { throw new UnsupportedOperationException("Cannot access the PhaseTracker off-thread, please use the respective PhaseTracker for their proper thread."); } return this.stack.peekState(); } public PhaseContext getPhaseContext() { - if (Thread.currentThread() != this.getSidedThread()) { + if (!this.onSidedThread()) { throw new UnsupportedOperationException("Cannot access the PhaseTracker off-thread, please use the respective PhaseTracker for their proper thread."); } return this.stack.peekContext(); @@ -321,7 +371,7 @@ public PhaseContext getPhaseContext() { // ----------------- STATE ACCESS ---------------------------------- void switchToPhase(final IPhaseState state, final PhaseContext phaseContext) { - if (phaseContext.createdTracker != this && Thread.currentThread() != this.getSidedThread()) { + if (phaseContext.createdTracker != this && !this.onSidedThread()) { // lol no, report the block change properly new PrettyPrinter(60).add("Illegal Async PhaseTracker Access").centre().hr() .addWrapped(PhasePrinter.ASYNC_TRACKER_ACCESS) @@ -334,7 +384,7 @@ void switchToPhase(final IPhaseState state, final PhaseContext phaseContex Objects.requireNonNull(state, "State cannot be null!"); Objects.requireNonNull(phaseContext, "PhaseContext cannot be null!"); Preconditions.checkArgument(phaseContext.isComplete(), "PhaseContext must be complete!"); - if (this == PhaseTracker.SERVER && SpongeConfigs.getCommon().get().phaseTracker.verbose) { + if (this != PhaseTracker.CLIENT && SpongeConfigs.getCommon().get().phaseTracker.verbose) { if (this.stack.size() > 6) { if (this.stack.checkForRunaways(state, phaseContext)) { PhasePrinter.printRunawayPhase(this.stack, state, phaseContext); @@ -351,7 +401,7 @@ void switchToPhase(final IPhaseState state, final PhaseContext phaseContex @SuppressWarnings({"rawtypes", "unused", "try"}) void completePhase(final PhaseContext context) { - if (context.createdTracker != this && Thread.currentThread() != this.getSidedThread()) { + if (context.createdTracker != this && !this.onSidedThread()) { // lol no, report the block change properly new PrettyPrinter(60).add("Illegal Async PhaseTracker Access").centre().hr() .addWrapped(PhasePrinter.ASYNC_TRACKER_ACCESS) @@ -765,7 +815,7 @@ public Optional removeContext(final EventContextKey key) { private void enforceMainThread() { // On clients, this may not be available immediately, we can't bomb out that early. - if (Thread.currentThread() != this.getSidedThread()) { + if (!this.onSidedThread()) { throw new IllegalStateException(String.format( "CauseStackManager called from off main thread (current='%s', expected='%s')!", ThreadUtil.getDescription(Thread.currentThread()), @@ -831,6 +881,33 @@ public CauseStackManager apiAccess() { return this.api; } + public T switchStartupRegistryHolder( + final SpongeRegistryHolder holder, final Supplier valueSupplier, + final Function exceptionalValueSupplier + ) { + final @Nullable SpongeRegistryHolder previousHolder = this.startupRegistryHolder(); + try { + this.startupRegistryHolder(holder); + return valueSupplier.get(); + } catch (final RuntimeException e) { + return exceptionalValueSupplier.apply(e); + } finally { + this.startupRegistryHolder(previousHolder); + } + } + + public void startupRegistryHolder(final @Nullable RegistryHolder holder) { + if (holder instanceof SpongeRegistryHolder srh) { + this.startupRegistryHolder = srh; + } else { + this.startupRegistryHolder = null; + } + } + + public @Nullable SpongeRegistryHolder startupRegistryHolder() { + return this.startupRegistryHolder; + } + /** * We insert implicit phases for plugin created frames. */ diff --git a/src/main/java/org/spongepowered/common/event/tracking/PooledPhaseState.java b/src/main/java/org/spongepowered/common/event/tracking/PooledPhaseState.java index 24e8ddce470..59b1ecb2a87 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/PooledPhaseState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/PooledPhaseState.java @@ -38,11 +38,11 @@ protected PooledPhaseState() { @Override public final C createPhaseContext(final PhaseTracker tracker) { - if (Thread.currentThread() != tracker.getSidedThread()) { + if (!tracker.onSidedThread()) { throw new IllegalStateException("Asynchronous Thread Access to PhaseTracker: " + tracker); } - if (tracker == PhaseTracker.SERVER) { + if (tracker == PhaseTracker.getServerInstanceExplicitly()) { if (this.serverCached != null) { final C cached = this.serverCached; this.serverCached = null; @@ -64,10 +64,10 @@ public final C createPhaseContext(final PhaseTracker tracker) { final void releaseContextFromPool(final C context) { final PhaseTracker createdTracker = context.createdTracker; - if (Thread.currentThread() != createdTracker.getSidedThread()) { + if (!createdTracker.onSidedThread()) { throw new IllegalStateException("Asynchronous Thread Access to PhaseTracker: " + createdTracker); } - if (createdTracker == PhaseTracker.SERVER) { + if (createdTracker == PhaseTracker.getServerInstanceExplicitly()) { if (this.serverCached == null) { this.serverCached = context; return; diff --git a/src/main/java/org/spongepowered/common/event/tracking/TrackingUtil.java b/src/main/java/org/spongepowered/common/event/tracking/TrackingUtil.java index 908234cabda..e023b93dec7 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/TrackingUtil.java +++ b/src/main/java/org/spongepowered/common/event/tracking/TrackingUtil.java @@ -103,7 +103,8 @@ public static void tickEntity(final net.minecraft.world.entity.Entity entity, fi return; } - final EntityTickContext tickContext = TickPhase.Tick.ENTITY.createPhaseContext(PhaseTracker.SERVER).source(entity); + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) entity.level()); + final EntityTickContext tickContext = TickPhase.Tick.ENTITY.createPhaseContext(phaseTracker).source(entity); try (final EntityTickContext context = tickContext) { if (entity instanceof CreatorTrackedBridge ctb) { ctb.tracker$getNotifierUUID().ifPresent(context::notifier); @@ -119,7 +120,7 @@ public static void tickEntity(final net.minecraft.world.entity.Entity entity, fi SpongeCommonEventFactory.callNaturalRotateEntityEvent(entity); } } catch (final Exception e) { - PhasePrinter.printExceptionFromPhase(PhaseTracker.getInstance().stack, e, tickContext); + PhasePrinter.printExceptionFromPhase(phaseTracker.stack, e, tickContext); } } @@ -133,7 +134,8 @@ public static void tickTileEntity(final BlockEntity blockEntity, final Runnable return; } - final TileEntityTickContext context = TickPhase.Tick.TILE_ENTITY.createPhaseContext(PhaseTracker.SERVER).source(blockEntity); + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) blockEntity.getLevel()); + final TileEntityTickContext context = TickPhase.Tick.TILE_ENTITY.createPhaseContext(phaseTracker).source(blockEntity); try (final PhaseContext<@NonNull ?> phaseContext = context) { if (blockEntity instanceof CreatorTrackedBridge ctb) { // Add notifier and owner so we don't have to perform lookups during the phases and other processing @@ -160,7 +162,7 @@ public static void tickTileEntity(final BlockEntity blockEntity, final Runnable } } catch (final Exception e) { - PhasePrinter.printExceptionFromPhase(PhaseTracker.getInstance().stack, e, context); + PhasePrinter.printExceptionFromPhase(phaseTracker.stack, e, context); } } @@ -169,10 +171,11 @@ public static void updateTickBlock( final BlockPos pos, final Runnable tick) { final ServerLevel world = (ServerLevel) mixinWorld; final org.spongepowered.api.world.server.ServerWorld apiWorld = (org.spongepowered.api.world.server.ServerWorld) world; + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(world); if (ShouldFire.TICK_BLOCK_EVENT) { final BlockSnapshot snapshot = mixinWorld.bridge$createSnapshot(block, pos, BlockChangeFlags.NONE); - final TickBlockEvent event = SpongeEventFactory.createTickBlockEventScheduled(PhaseTracker.getInstance().currentCause(), snapshot); + final TickBlockEvent event = SpongeEventFactory.createTickBlockEventScheduled(phaseTracker.currentCause(), snapshot); SpongeCommon.post(event); if (event.isCancelled()) { return; @@ -180,10 +183,10 @@ public static void updateTickBlock( } final LocatableBlock locatable = new SpongeLocatableBlockBuilder().world(apiWorld).position(pos.getX(), pos.getY(), pos.getZ()).state((BlockState)block).build(); - final BlockTickContext phaseContext = TickPhase.Tick.BLOCK.createPhaseContext(PhaseTracker.SERVER).source(locatable); + final BlockTickContext phaseContext = TickPhase.Tick.BLOCK.createPhaseContext(phaseTracker).source(locatable); // We have to associate any notifiers in case of scheduled block updates from other sources - final PhaseContext<@NonNull ?> currentContext = PhaseTracker.getInstance().getPhaseContext(); + final PhaseContext<@NonNull ?> currentContext = phaseTracker.getPhaseContext(); currentContext.appendNotifierPreBlockTick(world, pos, phaseContext); // Now actually switch to the new phase @@ -192,21 +195,20 @@ public static void updateTickBlock( PhaseTracker.LOGGER.trace(TrackingUtil.BLOCK_TICK, () -> "Wrapping Block Tick: " + block.toString()); tick.run(); } catch (final Exception | NoClassDefFoundError e) { - PhasePrinter.printExceptionFromPhase(PhaseTracker.getInstance().stack, e, phaseContext); + PhasePrinter.printExceptionFromPhase(phaseTracker.stack, e, phaseContext); } } public static void updateTickFluid( - final TrackedWorldBridge mixinWorld, final FluidState fluidState, final BlockPos pos, final Runnable tick - ) { + final TrackedWorldBridge mixinWorld, final FluidState fluidState, final BlockPos pos, final net.minecraft.world.level.block.state.BlockState blockState, final Runnable tick) { final ServerLevel world = (ServerLevel) mixinWorld; final org.spongepowered.api.world.server.ServerWorld apiWorld = (org.spongepowered.api.world.server.ServerWorld) world; + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(world); - final net.minecraft.world.level.block.state.BlockState blockState = fluidState.createLegacyBlock(); if (ShouldFire.TICK_BLOCK_EVENT) { final BlockSnapshot snapshot = mixinWorld.bridge$createSnapshot(blockState, pos, BlockChangeFlags.NONE); - final TickBlockEvent event = SpongeEventFactory.createTickBlockEventScheduled(PhaseTracker.getInstance().currentCause(), snapshot); + final TickBlockEvent event = SpongeEventFactory.createTickBlockEventScheduled(phaseTracker.currentCause(), snapshot); SpongeCommon.post(event); if (event.isCancelled()) { return; @@ -214,12 +216,12 @@ public static void updateTickFluid( } final LocatableBlock locatable = new SpongeLocatableBlockBuilder().world(apiWorld).position(pos.getX(), pos.getY(), pos.getZ()).state((BlockState) blockState).build(); - final FluidTickContext phaseContext = TickPhase.Tick.FLUID.createPhaseContext(PhaseTracker.SERVER) + final FluidTickContext phaseContext = TickPhase.Tick.FLUID.createPhaseContext(phaseTracker) .source(locatable) .fluid(fluidState); // We have to associate any notifiers in case of scheduled block updates from other sources - final PhaseContext<@NonNull ?> currentContext = PhaseTracker.getInstance().getPhaseContext(); + final PhaseContext<@NonNull ?> currentContext = phaseTracker.getPhaseContext(); currentContext.appendNotifierPreBlockTick(world, pos, phaseContext); // Now actually switch to the new phase @@ -228,7 +230,7 @@ public static void updateTickFluid( PhaseTracker.LOGGER.trace(TrackingUtil.FLUID_TICK, () -> "Wrapping Fluid Tick: " + fluidState.toString()); tick.run(); } catch (final Exception | NoClassDefFoundError e) { - PhasePrinter.printExceptionFromPhase(PhaseTracker.getInstance().stack, e, phaseContext); + PhasePrinter.printExceptionFromPhase(phaseTracker.stack, e, phaseContext); } } @@ -240,12 +242,13 @@ public static void randomTickBlock( ) { final ServerLevel world = (ServerLevel) mixinWorld; final org.spongepowered.api.world.server.ServerWorld apiWorld = (org.spongepowered.api.world.server.ServerWorld) world; + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(world); if (ShouldFire.TICK_BLOCK_EVENT) { final BlockSnapshot currentTickBlock = mixinWorld.bridge$createSnapshot(state, pos, BlockChangeFlags.NONE); final TickBlockEvent event = - SpongeEventFactory.createTickBlockEventRandom(PhaseTracker.getInstance().currentCause(), currentTickBlock); + SpongeEventFactory.createTickBlockEventRandom(phaseTracker.currentCause(), currentTickBlock); SpongeCommon.post(event); if (event.isCancelled()) { return; @@ -257,10 +260,10 @@ public static void randomTickBlock( .position(pos.getX(), pos.getY(), pos.getZ()) .state((BlockState) state) .build(); - final BlockTickContext phaseContext = TickPhase.Tick.RANDOM_BLOCK.createPhaseContext(PhaseTracker.SERVER).source(locatable); + final BlockTickContext phaseContext = TickPhase.Tick.RANDOM_BLOCK.createPhaseContext(phaseTracker).source(locatable); // We have to associate any notifiers in case of scheduled block updates from other sources - final PhaseContext<@NonNull ?> currentContext = PhaseTracker.getInstance().getPhaseContext(); + final PhaseContext<@NonNull ?> currentContext = phaseTracker.getPhaseContext(); currentContext.appendNotifierPreBlockTick(world, pos, phaseContext); // Now actually switch to the new phase try (final PhaseContext<@NonNull ?> context = phaseContext) { @@ -268,7 +271,7 @@ public static void randomTickBlock( PhaseTracker.LOGGER.trace(TrackingUtil.BLOCK_TICK, "Wrapping Random Block Tick: {}", state); tick.run(); } catch (final Exception | NoClassDefFoundError e) { - PhasePrinter.printExceptionFromPhase(PhaseTracker.getInstance().stack, e, phaseContext); + PhasePrinter.printExceptionFromPhase(phaseTracker.stack, e, phaseContext); } } @SuppressWarnings("rawtypes") @@ -279,12 +282,13 @@ public static void randomTickFluid( ) { final ServerLevel world = (ServerLevel) mixinWorld; final org.spongepowered.api.world.server.ServerWorld apiWorld = (org.spongepowered.api.world.server.ServerWorld) world; + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(world); if (ShouldFire.TICK_BLOCK_EVENT) { final BlockSnapshot currentTickBlock = mixinWorld.bridge$createSnapshot(state.createLegacyBlock(), pos, BlockChangeFlags.NONE); final TickBlockEvent event = - SpongeEventFactory.createTickBlockEventRandom(PhaseTracker.getInstance().currentCause(), currentTickBlock); + SpongeEventFactory.createTickBlockEventRandom(phaseTracker.currentCause(), currentTickBlock); SpongeCommon.post(event); if (event.isCancelled()) { return; @@ -296,13 +300,13 @@ public static void randomTickFluid( .position(pos.getX(), pos.getY(), pos.getZ()) .state((BlockState) state.createLegacyBlock()) .build(); - final FluidTickContext phaseContext = TickPhase.Tick.RANDOM_FLUID.createPhaseContext(PhaseTracker.SERVER) + final FluidTickContext phaseContext = TickPhase.Tick.RANDOM_FLUID.createPhaseContext(phaseTracker) .source(locatable) .fluid(state); // We have to associate any notifiers in case of scheduled block updates from other sources - final PhaseContext<@NonNull ?> currentContext = PhaseTracker.getInstance().getPhaseContext(); + final PhaseContext<@NonNull ?> currentContext = phaseTracker.getPhaseContext(); currentContext.appendNotifierPreBlockTick(world, pos, phaseContext); // Now actually switch to the new phase try (final PhaseContext<@NonNull ?> context = phaseContext) { @@ -310,12 +314,12 @@ public static void randomTickFluid( PhaseTracker.LOGGER.trace(TrackingUtil.FLUID_TICK, () -> "Wrapping Random Fluid Tick: " + state.toString()); tick.run(); } catch (final Exception | NoClassDefFoundError e) { - PhasePrinter.printExceptionFromPhase(PhaseTracker.getInstance().stack, e, phaseContext); + PhasePrinter.printExceptionFromPhase(phaseTracker.stack, e, phaseContext); } } public static boolean fireMinecraftBlockEvent( - final BlockEventData event, final BooleanSupplier tick + final ServerLevel worldIn, final BlockEventData event, final BooleanSupplier tick ) { final TrackableBlockEventDataBridge blockEvent = (TrackableBlockEventDataBridge) (Object) event; final @Nullable Object source = blockEvent.bridge$getTileEntity() != null ? blockEvent.bridge$getTileEntity() : blockEvent.bridge$getTickingLocatable(); @@ -323,8 +327,8 @@ public static boolean fireMinecraftBlockEvent( // No source present which means we are ignoring the phase state return tick.getAsBoolean(); } - final BlockEventTickContext phaseContext = TickPhase.Tick.BLOCK_EVENT.createPhaseContext(PhaseTracker.SERVER); - phaseContext.source(source); + final BlockEventTickContext phaseContext = TickPhase.Tick.BLOCK_EVENT.createPhaseContext(PhaseTracker.getWorldInstance(worldIn)); + phaseContext.source(blockEvent); final UUID user = ((TrackableBlockEventDataBridge) (Object) event).bridge$getSourceUserUUID(); if (user != null) { diff --git a/src/main/java/org/spongepowered/common/event/tracking/UnwindingState.java b/src/main/java/org/spongepowered/common/event/tracking/UnwindingState.java index 76c80f4ebcb..20ccedb4e1d 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/UnwindingState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/UnwindingState.java @@ -90,7 +90,7 @@ public void unwind(final UnwindingPhaseContext context) { @Override public Supplier attemptWorldKey(UnwindingPhaseContext context) { - final Optional> o = context.getSource(Entity.class).map(s -> () -> (ResourceKey) (Object) s.level().dimension().location()); + final Optional> o = context.getSource(Entity.class).map(s -> () -> (ResourceKey) (Object) s.level().dimension().identifier()); return o.orElseThrow(() -> new IllegalStateException("Expected to be ticking an entity!")); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/EffectTransactor.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/EffectTransactor.java index cabf97ef8ca..b1e025c7e73 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/EffectTransactor.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/EffectTransactor.java @@ -30,13 +30,13 @@ import java.util.Deque; public class EffectTransactor implements AutoCloseable { - final @Nullable ResultingTransactionBySideEffect previousEffect; + final @Nullable ResultingTransactionBySideEffect previousEffect; public final @Nullable GameTransaction<@NonNull ?> parent; private final TransactionalCaptureSupplier supplier; - private final ResultingTransactionBySideEffect effect; + private final ResultingTransactionBySideEffect effect; - EffectTransactor(final ResultingTransactionBySideEffect effect, final @Nullable GameTransaction<@NonNull ?> parent, - final @Nullable ResultingTransactionBySideEffect previousEffect, final TransactionalCaptureSupplier transactor) { + EffectTransactor(final ResultingTransactionBySideEffect effect, final @Nullable GameTransaction<@NonNull ?> parent, + final @Nullable ResultingTransactionBySideEffect previousEffect, final TransactionalCaptureSupplier transactor) { /* | ChangeBlock(1) <- head will be RemoveTileEntity(1), tail is still RemoveTileentity(1) | |- RemoveTileEntity <- Head will be ChangeBlock(2) tail is still ChangeBlock(2) @@ -55,7 +55,7 @@ public void close() { && this.parent.sideEffects != null && this.parent.getEffects().peekLast() == this.effect ) { - final Deque effects = this.parent.getEffects(); + final Deque> effects = this.parent.getEffects(); effects.removeLast(); if (effects.isEmpty()) { this.parent.sideEffects = null; diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/EventByTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/EventByTransaction.java index eed31f53d27..a2dc78e740c 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/EventByTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/EventByTransaction.java @@ -32,7 +32,7 @@ import org.spongepowered.api.event.Event; @DefaultQualifier(NonNull.class) -record EventByTransaction( +public record EventByTransaction( T event, ImmutableList> transactions, @Nullable GameTransaction<@NonNull ?> parent, diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/GameTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/GameTransaction.java index cb50e5af272..9f524c74b75 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/GameTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/GameTransaction.java @@ -46,6 +46,7 @@ import java.util.Optional; import java.util.StringJoiner; import java.util.function.BiConsumer; +import java.util.stream.Stream; @DefaultQualifier(NonNull.class) public abstract class GameTransaction implements TransactionFlow, StatefulTransaction { @@ -54,7 +55,7 @@ public abstract class GameTransaction implements protected boolean cancelled = false; // Children Definitions - @Nullable LinkedList sideEffects; + @Nullable LinkedList> sideEffects; // LinkedList node definitions @Nullable GameTransaction<@NonNull ?> previous; @@ -75,14 +76,14 @@ public final TransactionType getTransactionType() { return this.transactionType; } - final Deque getEffects() { + final Deque> getEffects() { if (this.sideEffects == null) { this.sideEffects = new LinkedList<>(); } return this.sideEffects; } - public final void addLast(final ResultingTransactionBySideEffect effect) { + public final void addLast(final ResultingTransactionBySideEffect effect) { if (this.sideEffects == null) { this.sideEffects = new LinkedList<>(); } @@ -97,7 +98,7 @@ public final boolean hasAnyPrimaryChildrenTransactions() { if (this.sideEffects == null) { return false; } - for (final ResultingTransactionBySideEffect sideEffect : this.sideEffects) { + for (final ResultingTransactionBySideEffect sideEffect : this.sideEffects) { @Nullable GameTransaction<@NonNull ?> transaction = sideEffect.head; while (transaction != null) { if (transaction.transactionType.isPrimary() || transaction.hasChildTransactions()) { @@ -131,10 +132,17 @@ public abstract Optional generateEvent( public final void markCancelled() { this.cancelled = true; this.childIterator().forEachRemaining(GameTransaction::markCancelled); + if (this.next != null && this.next.hasUnknownChainRequiringCancellation()) { + this.next.markCancelled(); + } } public abstract boolean markCancelledTransactions(E event, ImmutableList> transactions); + protected boolean hasUnknownChainRequiringCancellation() { + return false; + } + public void postProcessEvent(final PhaseContext<@NonNull ?> context, final E event) { } @@ -182,17 +190,29 @@ protected void captureState() { } + public void associateSideEffectEvents(E e, Stream elements) { + + } + + public void pushCause(CauseStackManager.StackFrame frame, E e) { + frame.pushCause(e); + } + + public void finalizeSideEffects(E e) { + + } + private static class ChildIterator implements Iterator> { - private final Iterator effectIterator; + private final Iterator> effectIterator; private @Nullable GameTransaction<@NonNull ?> cachedNext; private @MonotonicNonNull GameTransaction<@NonNull ?> pointer; private boolean hasNoRemainingElements = false; - ChildIterator(final Iterator iterator) { + ChildIterator(final Iterator> iterator) { // We're going to search the iterator's effects until we find the first at least this.effectIterator = iterator; while (this.effectIterator.hasNext()) { - final ResultingTransactionBySideEffect next = this.effectIterator.next(); + final ResultingTransactionBySideEffect next = this.effectIterator.next(); if (next.head != null) { this.cachedNext = next.head; this.pointer = next.head; @@ -219,7 +239,7 @@ public boolean hasNext() { // start search for the next, sadly because effects don't make a clean chain, // there can be many effects with no transactions recorded while (this.effectIterator.hasNext()) { - final ResultingTransactionBySideEffect next = this.effectIterator.next(); + final ResultingTransactionBySideEffect next = this.effectIterator.next(); if (next.head != null) { this.cachedNext = next.head; return true; @@ -250,16 +270,16 @@ public boolean hasNext() { private static class ReverseChildIterator implements Iterator> { - private final Iterator effectIterator; + private final Iterator> effectIterator; private @Nullable GameTransaction<@NonNull ?> cachedPrevious; private @MonotonicNonNull GameTransaction<@NonNull ?> pointer; private boolean hasNoRemainingElements = false; - ReverseChildIterator(final Iterator iterator) { + ReverseChildIterator(final Iterator> iterator) { // We're going to search the iterator's effects until we find the first at least this.effectIterator = iterator; while (this.effectIterator.hasNext()) { - final ResultingTransactionBySideEffect next = this.effectIterator.next(); + final ResultingTransactionBySideEffect next = this.effectIterator.next(); if (next.tail != null) { this.pointer = next.tail; this.cachedPrevious = next.tail; @@ -287,7 +307,7 @@ public boolean hasNext() { // start search for the next, sadly because effects don't make a clean chain, // there can be many effects with no transactions recorded while (this.effectIterator.hasNext()) { - final ResultingTransactionBySideEffect next = this.effectIterator.next(); + final ResultingTransactionBySideEffect next = this.effectIterator.next(); if (next.tail != null) { this.cachedPrevious = next.tail; return true; diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/ResultingTransactionBySideEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/ResultingTransactionBySideEffect.java index c224628dbbc..3cae058b582 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/ResultingTransactionBySideEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/ResultingTransactionBySideEffect.java @@ -33,12 +33,12 @@ import java.util.Iterator; import java.util.Optional; -public class ResultingTransactionBySideEffect { - public final ProcessingSideEffect effect; +public class ResultingTransactionBySideEffect { + public final ProcessingSideEffect effect; @Nullable GameTransaction<@NonNull ?> head; @Nullable GameTransaction<@NonNull ?> tail; - public ResultingTransactionBySideEffect(final ProcessingSideEffect effect) { + public ResultingTransactionBySideEffect(final ProcessingSideEffect effect) { this.effect = effect; } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionSink.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionSink.java index e9aceccb2ec..a10a5bf25c8 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionSink.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionSink.java @@ -30,12 +30,11 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.CraftingRecipe; -import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; @@ -45,6 +44,8 @@ import net.minecraft.world.ticks.ScheduledTick; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.block.BlockSnapshot; +import org.spongepowered.api.event.block.InteractBlockEvent; import org.spongepowered.api.event.cause.entity.SpawnType; import org.spongepowered.api.item.inventory.Inventory; import org.spongepowered.api.item.inventory.ItemStackSnapshot; @@ -52,6 +53,7 @@ import org.spongepowered.api.item.inventory.crafting.CraftingInventory; import org.spongepowered.api.item.inventory.transaction.SlotTransaction; import org.spongepowered.api.scheduler.ScheduledUpdate; +import org.spongepowered.api.util.Direction; import org.spongepowered.api.world.BlockChangeFlag; import org.spongepowered.api.world.BlockChangeFlags; import org.spongepowered.common.SpongeCommon; @@ -72,8 +74,10 @@ import org.spongepowered.common.event.tracking.context.transaction.block.ScheduleUpdateTransaction; import org.spongepowered.common.event.tracking.context.transaction.effect.BlockAddedEffect; import org.spongepowered.common.event.tracking.context.transaction.effect.EntityPerformingDropsEffect; +import org.spongepowered.common.event.tracking.context.transaction.effect.InteractionItemEffect; import org.spongepowered.common.event.tracking.context.transaction.effect.InventoryEffect; import org.spongepowered.common.event.tracking.context.transaction.effect.PrepareBlockDrops; +import org.spongepowered.common.event.tracking.context.transaction.effect.ProcessingSideEffect; import org.spongepowered.common.event.tracking.context.transaction.inventory.ClickCreativeMenuTransaction; import org.spongepowered.common.event.tracking.context.transaction.inventory.ClickMenuTransaction; import org.spongepowered.common.event.tracking.context.transaction.inventory.CloseMenuTransaction; @@ -82,6 +86,8 @@ import org.spongepowered.common.event.tracking.context.transaction.inventory.CraftingTransaction; import org.spongepowered.common.event.tracking.context.transaction.inventory.DropFromPlayerInventoryTransaction; import org.spongepowered.common.event.tracking.context.transaction.inventory.ExplicitInventoryOmittedTransaction; +import org.spongepowered.common.event.tracking.context.transaction.inventory.InteractItemTransaction; +import org.spongepowered.common.event.tracking.context.transaction.inventory.InteractItemWithBlockTransaction; import org.spongepowered.common.event.tracking.context.transaction.inventory.InventoryTransaction; import org.spongepowered.common.event.tracking.context.transaction.inventory.OpenMenuTransaction; import org.spongepowered.common.event.tracking.context.transaction.inventory.PlaceCookingRecipeTransaction; @@ -100,6 +106,7 @@ import org.spongepowered.common.world.BlockChange; import org.spongepowered.common.world.SpongeBlockChangeFlag; import org.spongepowered.common.world.volume.VolumeStreamUtils; +import org.spongepowered.math.vector.Vector3d; import java.lang.ref.WeakReference; import java.util.Objects; @@ -124,9 +131,9 @@ interface TransactionSink { @Deprecated @Nullable EffectTransactor logTransaction(StatefulTransaction transaction); - EffectTransactor pushEffect(final ResultingTransactionBySideEffect effect); + EffectTransactor pushEffect(final ResultingTransactionBySideEffect effect); - EffectTransactor pushEffect(final ResultingTransactionBySideEffect effect, final GameTransaction parentTransaction); + EffectTransactor pushEffect(final ResultingTransactionBySideEffect effect, final GameTransaction parentTransaction); default ChangeBlock logBlockChange(final SpongeBlockSnapshot originalBlockSnapshot, final BlockState newState, final BlockChangeFlag flags @@ -180,7 +187,7 @@ default void logBlockEvent( final PrepareBlockDropsTransaction transaction = new PrepareBlockDropsTransaction(pos, state, original); final @Nullable EffectTransactor effect = this.logTransaction(transaction); if (effect == null) { - return this.pushEffect(new ResultingTransactionBySideEffect(PrepareBlockDrops.getInstance())); + return this.pushEffect(new ResultingTransactionBySideEffect<>(PrepareBlockDrops.getInstance())); } return effect; } @@ -267,7 +274,7 @@ default boolean logTileRemoval(final @Nullable BlockEntity tileentity, final Sup final EntityPerformingDropsTransaction transaction = new EntityPerformingDropsTransaction(entity); this.logTransaction(transaction); if (transaction.recorded()) { - return this.pushEffect(new ResultingTransactionBySideEffect(EntityPerformingDropsEffect.getInstance())); + return this.pushEffect(new ResultingTransactionBySideEffect<>(EntityPerformingDropsEffect.getInstance())); } return null; } @@ -334,7 +341,7 @@ default void logPlayerInventoryChange(final Player player, final PlayerInventory } default EffectTransactor logClickContainer( - final AbstractContainerMenu menu, final int slotNum, final int buttonNum, final ClickType clickType, final Player player + final AbstractContainerMenu menu, final int slotNum, final int buttonNum, final ContainerInput clickType, final Player player ) { @Nullable Slot slot = null; if (buttonNum >= 0) { // Try to get valid slot - might not be present e.g. for drag-events @@ -343,41 +350,41 @@ default EffectTransactor logClickContainer( final ClickMenuTransaction transaction = new ClickMenuTransaction( player, menu, slotNum, buttonNum, clickType, slot, ItemStackUtil.snapshotOf(player.containerMenu.getCarried())); this.logTransaction(transaction); - return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance())); + return this.pushEffect(new ResultingTransactionBySideEffect<>(InventoryEffect.getInstance())); } default EffectTransactor logPlayerInventoryChangeWithEffect(final Player player, final PlayerInventoryTransaction.EventCreator eventCreator) { final PlayerInventoryTransaction transaction = new PlayerInventoryTransaction(player, eventCreator); this.logTransaction(transaction); - return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance())); + return this.pushEffect(new ResultingTransactionBySideEffect<>(InventoryEffect.getInstance())); } default EffectTransactor logCreativeClickContainer(final int slotNum, final ItemStackSnapshot creativeStack, final Player player) { final ClickCreativeMenuTransaction transaction = new ClickCreativeMenuTransaction(player, slotNum, creativeStack); this.logTransaction(transaction); - return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance())); + return this.pushEffect(new ResultingTransactionBySideEffect<>(InventoryEffect.getInstance())); } default EffectTransactor logDropFromPlayerInventory(final ServerPlayer player, final boolean dropAll) { final DropFromPlayerInventoryTransaction transaction = new DropFromPlayerInventoryTransaction(player, dropAll); this.logTransaction(transaction); - return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance())); + return this.pushEffect(new ResultingTransactionBySideEffect<>(InventoryEffect.getInstance())); } default EffectTransactor logOpenInventory(final Player player) { final OpenMenuTransaction transaction = new OpenMenuTransaction(player); this.logTransaction(transaction); - return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance())); + return this.pushEffect(new ResultingTransactionBySideEffect<>(InventoryEffect.getInstance())); } default EffectTransactor logCloseInventory(final PhaseContext<@NonNull ?> current, final Player player) { - final CloseMenuTransaction transaction = new CloseMenuTransaction(player, current.isClientSide()); + final CloseMenuTransaction transaction = new CloseMenuTransaction(player); this.logTransaction(transaction); - return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance())); + return this.pushEffect(new ResultingTransactionBySideEffect<>(InventoryEffect.getInstance())); } - default EffectTransactor logPlaceRecipe(final boolean shift, final RecipeHolder> recipe, final ServerPlayer player, final CraftingInventory craftInv) { + default EffectTransactor logPlaceRecipe(final boolean shift, final RecipeHolder recipe, final ServerPlayer player, final CraftingInventory craftInv) { final PlaceCraftingRecipeTransaction transaction = new PlaceCraftingRecipeTransaction(player, shift, recipe, craftInv); this.logTransaction(transaction); return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance())); @@ -386,7 +393,7 @@ default EffectTransactor logPlaceRecipe(final boolean shift, final RecipeHolder< default EffectTransactor logPlaceCookingRecipe(final BlockEntity blockEntity, boolean shift, final RecipeHolder recipe, final ServerPlayer player) { final PlaceCookingRecipeTransaction transaction = new PlaceCookingRecipeTransaction(blockEntity, player, shift, recipe); this.logTransaction(transaction); - return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance())); + return this.pushEffect(new ResultingTransactionBySideEffect<>(InventoryEffect.getInstance())); } default void logSelectTrade(final ServerPlayer player, final int item) { @@ -418,12 +425,36 @@ default void logCrafting(final Player player, @Nullable final ItemStack craftedS default EffectTransactor logIgnoredInventory(AbstractContainerMenu containerMenu) { final ExplicitInventoryOmittedTransaction transaction = new ExplicitInventoryOmittedTransaction(containerMenu); this.logTransaction(transaction); - return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance())); + return this.pushEffect(new ResultingTransactionBySideEffect<>(InventoryEffect.getInstance())); } default EffectTransactor logInventoryTransaction(final AbstractContainerMenu containerMenu) { final InventoryTransaction transaction = new InventoryTransaction((Inventory) containerMenu); this.logTransaction(transaction); - return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance())); + return this.pushEffect(new ResultingTransactionBySideEffect<>(InventoryEffect.getInstance())); } + + default void logSecondaryInteractionTransaction( + final ServerPlayer playerIn, final Vector3d hitVec, + final BlockSnapshot snapshot, final Direction direction, InteractBlockEvent.Secondary.Pre event) { + final InteractItemWithBlockTransaction transaction = new InteractItemWithBlockTransaction(playerIn, + hitVec, + snapshot, + direction, + event.originalUseBlockResult(), + event.useBlockResult(), + event.originalUseItemResult(), + event.useItemResult() + ); + this.logTransaction(transaction); + } + + default EffectTransactor logSecondaryInteractItemTransaction( + final ServerPlayer playerIn, final ItemStack stackIn + ) { + final InteractItemTransaction transaction = new InteractItemTransaction(playerIn, stackIn); + this.logTransaction(transaction); + return this.pushEffect(new ResultingTransactionBySideEffect<>(InteractionItemEffect.getInstance())); + } + } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionalCaptureSupplier.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionalCaptureSupplier.java index 9666931b325..67c27274322 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionalCaptureSupplier.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionalCaptureSupplier.java @@ -29,6 +29,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.units.qual.A; +import org.checkerframework.checker.units.qual.C; import org.spongepowered.api.Sponge; import org.spongepowered.api.event.Cancellable; import org.spongepowered.api.event.CauseStackManager; @@ -37,6 +39,7 @@ import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.event.tracking.context.ICaptureSupplier; +import org.spongepowered.common.event.tracking.context.transaction.effect.ProcessingSideEffect; import org.spongepowered.common.event.tracking.context.transaction.type.TransactionType; import java.util.Collections; @@ -93,15 +96,15 @@ public boolean isEmpty() { */ @Override - public EffectTransactor pushEffect(final ResultingTransactionBySideEffect effect) { + public EffectTransactor pushEffect(final ResultingTransactionBySideEffect effect) { final GameTransaction<@NonNull ?> parentTransaction = Optional.ofNullable(this.effect) - .map(child -> (GameTransaction) child.tail) + .map(child -> child.tail) .orElse(Objects.requireNonNull(this.tail, "Somehow pushing a new effect without an owning Transaction")); return this.pushEffect(effect, parentTransaction); } @Override - public EffectTransactor pushEffect(final ResultingTransactionBySideEffect effect, final GameTransaction parentTransaction) { + public EffectTransactor pushEffect(final ResultingTransactionBySideEffect effect, final GameTransaction parentTransaction) { final EffectTransactor effectTransactor = new EffectTransactor(effect, parentTransaction, this.effect, this); this.effect = effect; parentTransaction.addLast(effect); @@ -305,13 +308,16 @@ private static void generateEventForTransaction( if (transaction.sideEffects == null || transaction.sideEffects.isEmpty()) { continue; } - generatedEvent.ifPresent(frame::pushCause); + generatedEvent.ifPresent(e -> transaction.pushCause(frame, e)); for (final ResultingTransactionBySideEffect sideEffect : transaction.sideEffects) { if (sideEffect.head == null) { continue; } - builder.addAll(TransactionalCaptureSupplier.batchTransactions(sideEffect.head, pointer, context, transactionPostEventBuilder)); + final var elements = TransactionalCaptureSupplier.batchTransactions(sideEffect.head, pointer, context, transactionPostEventBuilder); + generatedEvent.ifPresent(e -> transaction.associateSideEffectEvents(e, elements.stream().map(EventByTransaction::event))); + builder.addAll(elements); } + generatedEvent.ifPresent(transaction::finalizeSideEffects); } } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/block/ChangeBlock.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/block/ChangeBlock.java index 99b4afb6ddc..9e9003ef299 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/block/ChangeBlock.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/block/ChangeBlock.java @@ -75,7 +75,7 @@ public ChangeBlock( this.original = attachedSnapshot; this.newState = newState; this.blockChangeFlag = blockChange; - this.originalOpacity = this.originalState.getLightBlock(this.original.getServerWorld().get(), this.affectedPosition); + this.originalOpacity = this.originalState.getLightDampening(); } public BlockState getNewState() { diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/AddBlockLootDropsEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/AddBlockLootDropsEffect.java index 51e1a40d109..5b431acffe9 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/AddBlockLootDropsEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/AddBlockLootDropsEffect.java @@ -40,7 +40,7 @@ import org.spongepowered.common.util.VecHelper; import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class AddBlockLootDropsEffect implements ProcessingSideEffect { +public final class AddBlockLootDropsEffect implements ProcessingSideEffect { private static final class Holder { static final AddBlockLootDropsEffect INSTANCE = new AddBlockLootDropsEffect(); @@ -53,15 +53,17 @@ public static AddBlockLootDropsEffect getInstance() { AddBlockLootDropsEffect() {} @Override - public EffectResult processSideEffect( - final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, final SpongeBlockChangeFlag flag, - final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { + final BlockState newState = args.newState(); + final SpongeBlockChangeFlag flag = args.flag(); + final int limit = args.limit(); final PhaseContext<@NonNull ?> phaseContext = PhaseTracker.getInstance().getPhaseContext(); final ServerLevel world = pipeline.getServerWorld(); - final @Nullable BlockEntity existingTile = oldState.tileEntity; - final BlockPos pos = oldState.pos; + final @Nullable BlockEntity existingTile = oldState.tileEntity(); + final BlockPos pos = oldState.pos(); final LootParams.Builder lootBuilder = new LootParams.Builder(world) .withParameter(LootContextParams.ORIGIN, VecHelper.toVanillaVector3d(pos)) @@ -70,6 +72,6 @@ public EffectResult processSideEffect( phaseContext.populateLootContext(lootBuilder); - return new EffectResult(newState, oldState.state.getDrops(lootBuilder), false); + return new EffectResult<>(newState, oldState.state().getDrops(lootBuilder), false); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/BlockAddedEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/BlockAddedEffect.java index bb2a8c66667..9c7daa20f57 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/BlockAddedEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/BlockAddedEffect.java @@ -25,11 +25,11 @@ package org.spongepowered.common.event.tracking.context.transaction.effect; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class BlockAddedEffect implements ProcessingSideEffect { +public final class BlockAddedEffect implements ProcessingSideEffect { private static final class Holder { static final BlockAddedEffect INSTANCE = new BlockAddedEffect(); @@ -42,9 +42,11 @@ public static BlockAddedEffect getInstance() { } @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { + final var flag = args.flag(); + final var newState = args.newState(); // Vanilla will check if this is the server // if (!this.level.isClientSide) { // var2.onPlace(this.level, var1, var11, var3); @@ -53,8 +55,8 @@ public EffectResult processSideEffect(final BlockPipeline pipeline, final Pipeli // so we wrap the onPlace with the flag check (also, we're always on the // server in this context). if (flag.performBlockPhysics()) { - newState.onPlace(pipeline.getServerWorld(), oldState.pos, oldState.state, flag.movingBlocks()); + newState.onPlace(pipeline.getServerWorld(), oldState.pos(), oldState.state(), flag.movingBlocks()); } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/bridge/world/level/block/entity/CampfireBlockEntityBridge.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/BlockChangeArgs.java similarity index 78% rename from src/main/java/org/spongepowered/common/bridge/world/level/block/entity/CampfireBlockEntityBridge.java rename to src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/BlockChangeArgs.java index 39c1a505ab7..2a572a21dc3 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/level/block/entity/CampfireBlockEntityBridge.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/BlockChangeArgs.java @@ -22,12 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.bridge.world.level.block.entity; +package org.spongepowered.common.event.tracking.context.transaction.effect; -import net.minecraft.world.item.crafting.CampfireCookingRecipe; -import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.level.block.state.BlockState; +import org.spongepowered.common.world.SpongeBlockChangeFlag; -public interface CampfireBlockEntityBridge { +public record BlockChangeArgs( + BlockState newState, + SpongeBlockChangeFlag flag, + int limit +) implements ProcessingSideEffect.Args { - void bridge$placeRecipe(RecipeHolder recipe); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/BroadcastInventoryChangesEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/BroadcastInventoryChangesEffect.java index 8664482ce9b..5d4300c3fb2 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/BroadcastInventoryChangesEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/BroadcastInventoryChangesEffect.java @@ -25,14 +25,14 @@ package org.spongepowered.common.event.tracking.context.transaction.effect; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; import org.spongepowered.common.event.tracking.context.transaction.ResultingTransactionBySideEffect; import org.spongepowered.common.event.tracking.context.transaction.TransactionalCaptureSupplier; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class BroadcastInventoryChangesEffect implements ProcessingSideEffect { +public final class BroadcastInventoryChangesEffect implements ProcessingSideEffect { private static final class Holder { static final BroadcastInventoryChangesEffect INSTANCE = new BroadcastInventoryChangesEffect(); @@ -41,16 +41,16 @@ public static BroadcastInventoryChangesEffect getInstance() { return Holder.INSTANCE; } public static EffectTransactor transact(final TransactionalCaptureSupplier transactor) { - return transactor.pushEffect(new ResultingTransactionBySideEffect(BroadcastInventoryChangesEffect.getInstance())); + return transactor.pushEffect(new ResultingTransactionBySideEffect<>(BroadcastInventoryChangesEffect.getInstance())); } BroadcastInventoryChangesEffect() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, - final BlockState newState, final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { - return EffectResult.NULL_RETURN; + return EffectResult.nullReturn(); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/CheckBlockPostPlacementIsSameEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/CheckBlockPostPlacementIsSameEffect.java index f3693379738..378827bf319 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/CheckBlockPostPlacementIsSameEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/CheckBlockPostPlacementIsSameEffect.java @@ -26,11 +26,11 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunkSection; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class CheckBlockPostPlacementIsSameEffect implements ProcessingSideEffect { +public final class CheckBlockPostPlacementIsSameEffect implements ProcessingSideEffect { private static final class Holder { static final CheckBlockPostPlacementIsSameEffect INSTANCE = new CheckBlockPostPlacementIsSameEffect(); } @@ -40,17 +40,18 @@ public static CheckBlockPostPlacementIsSameEffect getInstance() { } @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { + final var newState = args.newState(); final LevelChunkSection chunkSection = pipeline.getAffectedSection(); - final int x = oldState.pos.getX() & 15; - final int y = oldState.pos.getY() & 15; - final int z = oldState.pos.getZ() & 15; + final int x = oldState.pos().getX() & 15; + final int y = oldState.pos().getY() & 15; + final int z = oldState.pos().getZ() & 15; final BlockState currentState = chunkSection.getBlockState(x, y, z); if (!currentState.is(newState.getBlock())) { - return new EffectResult(newState, true); + return new EffectResult<>(newState, true); } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ChunkChangeCompleteEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ChunkChangeCompleteEffect.java index eecb265b5b7..4d705e43610 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ChunkChangeCompleteEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ChunkChangeCompleteEffect.java @@ -26,15 +26,16 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class ChunkChangeCompleteEffect implements ProcessingSideEffect { +public final class ChunkChangeCompleteEffect implements ProcessingSideEffect { private static final class Holder { static final ChunkChangeCompleteEffect INSTANCE = new ChunkChangeCompleteEffect(); } + ChunkChangeCompleteEffect() { } @@ -43,13 +44,12 @@ public static ChunkChangeCompleteEffect getInstance() { } @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { - final LevelChunk chunk = pipeline.getAffectedChunk(); // this.unsaved = true; // Vanilla, we'll just call the accessor available - chunk.setUnsaved(true); - return new EffectResult(oldState.state, true); + chunk.markUnsaved(); + return new EffectResult<>(oldState.state(), true); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/EffectResult.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/EffectResult.java index 3ceacfb2e23..e6076ae2b0b 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/EffectResult.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/EffectResult.java @@ -25,30 +25,35 @@ package org.spongepowered.common.event.tracking.context.transaction.effect; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collections; import java.util.List; -public final class EffectResult { +public final class EffectResult<@Nullable R> { - public static final EffectResult NULL_RETURN = new EffectResult(null, true); - public static final EffectResult NULL_PASS = new EffectResult(null, false); + public static <@Nullable T> EffectResult nullReturn() { + return new EffectResult<>(null, true); + } + + public static <@Nullable T> EffectResult nullPass() { + return new EffectResult<>(null, false); + } - public final @Nullable BlockState resultingState; + public final @Nullable R resultingState; public final List drops; public final boolean hasResult; - public EffectResult(final @Nullable BlockState resultingState, final boolean hasResult) { + public EffectResult(final @Nullable R resultingState, final boolean hasResult) { this.resultingState = resultingState; this.hasResult = hasResult; this.drops = Collections.emptyList(); } - public EffectResult(final @Nullable BlockState resultingState, final List drops, final boolean hasResult) { + public EffectResult(final @Nullable R resultingState, final List drops, final boolean hasResult) { this.resultingState = resultingState; this.drops = drops; this.hasResult = hasResult; } + } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/EntityPerformingDropsEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/EntityPerformingDropsEffect.java index eeb7ee4e4e5..4d9d4065a3d 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/EntityPerformingDropsEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/EntityPerformingDropsEffect.java @@ -25,11 +25,11 @@ package org.spongepowered.common.event.tracking.context.transaction.effect; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class EntityPerformingDropsEffect implements ProcessingSideEffect { +public final class EntityPerformingDropsEffect implements ProcessingSideEffect { private static final class Holder { static final EntityPerformingDropsEffect INSTANCE = new EntityPerformingDropsEffect(); @@ -40,10 +40,10 @@ public static EntityPerformingDropsEffect getInstance() { EntityPerformingDropsEffect() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, - final BlockState newState, final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { - return EffectResult.NULL_RETURN; + return EffectResult.nullReturn(); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ExplodeBlockEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ExplodeBlockEffect.java index baaf064fadd..af08550e265 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ExplodeBlockEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ExplodeBlockEffect.java @@ -28,14 +28,14 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.state.BlockState; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; import org.spongepowered.common.event.tracking.phase.general.ExplosionContext; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class ExplodeBlockEffect implements ProcessingSideEffect { +public final class ExplodeBlockEffect implements ProcessingSideEffect { private static final class Holder { static final ExplodeBlockEffect INSTANCE = new ExplodeBlockEffect(); @@ -47,20 +47,18 @@ public static ExplodeBlockEffect getInstance() { ExplodeBlockEffect() {} - @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public EffectResult processSideEffect( - final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, final SpongeBlockChangeFlag flag, - final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { final PhaseContext<@NonNull ?> phaseContext = PhaseTracker.getInstance().getPhaseContext(); final ServerLevel world = pipeline.getServerWorld(); - final BlockPos pos = oldState.pos; - if (phaseContext instanceof ExplosionContext) { - oldState.state.getBlock().wasExploded(world, pos, ((ExplosionContext) phaseContext).getExplosion()); + final BlockPos pos = oldState.pos(); + if (phaseContext instanceof ExplosionContext ec) { + oldState.state().getBlock().wasExploded(world, pos, ec.getExplosion()); } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InteractionAtArgs.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InteractionAtArgs.java new file mode 100644 index 00000000000..5cd9000fdc3 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InteractionAtArgs.java @@ -0,0 +1,42 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.context.transaction.effect; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; + +public record InteractionAtArgs( + Level world, + ServerPlayer player, + InteractionHand hand, + BlockHitResult blockRaytraceResult, + BlockState blockstate, + ItemStack copiedStack +) implements ProcessingSideEffect.Args { +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InteractionItemEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InteractionItemEffect.java new file mode 100644 index 00000000000..59a90650401 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InteractionItemEffect.java @@ -0,0 +1,58 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.context.transaction.effect; + +import net.minecraft.world.InteractionResult; +import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; +import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; +import org.spongepowered.common.event.tracking.context.transaction.pipeline.InteractionPipeline; + +public final class InteractionItemEffect implements ProcessingSideEffect.Simple, InteractionAtArgs, InteractionResult> { + + private static final class Holder { + static final InteractionItemEffect INSTANCE = new InteractionItemEffect(); + } + + public static InteractionItemEffect getInstance() { + return Holder.INSTANCE; + } + + @Override + public EffectResult processSideEffect( + InteractionPipeline pipeline, InteractionResult oldState, InteractionAtArgs args + ) { + final var player = args.player(); + final var world = args.world(); + final var blockstate = args.blockstate(); + final var blockHitResult = args.blockRaytraceResult(); + final InteractionResult result = blockstate.useWithoutItem(world, player, blockHitResult); + pipeline.transactor().logPlayerInventoryChange(args.player(), PlayerInventoryTransaction.EventCreator.STANDARD); + try (EffectTransactor ignored = BroadcastInventoryChangesEffect.transact(pipeline.transactor())) { + args.player().containerMenu.broadcastChanges(); + } + return new EffectResult<>(result, true); + } + +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InteractionUseItemOnBlockEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InteractionUseItemOnBlockEffect.java new file mode 100644 index 00000000000..c6180c95510 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InteractionUseItemOnBlockEffect.java @@ -0,0 +1,59 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.context.transaction.effect; + +import net.minecraft.world.InteractionResult; +import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; +import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; +import org.spongepowered.common.event.tracking.context.transaction.pipeline.InteractionPipeline; + +public final class InteractionUseItemOnBlockEffect implements ProcessingSideEffect, InteractionResult, InteractionAtArgs, InteractionResult> { + + private static final class Holder { + static final InteractionUseItemOnBlockEffect INSTANCE = new InteractionUseItemOnBlockEffect(); + } + + public static InteractionUseItemOnBlockEffect getInstance() { + return Holder.INSTANCE; + } + + @Override + public EffectResult processSideEffect( + InteractionPipeline pipeline, InteractionResult oldState, InteractionAtArgs args + ) { + final var player = args.player(); + final var hand = args.hand(); + final var world = args.world(); + final var blockRaytraceResult = args.blockRaytraceResult(); + final var blockstate = args.blockstate(); + final InteractionResult result = blockstate.useItemOn(args.copiedStack(), world, player, hand, blockRaytraceResult); + pipeline.transactor().logPlayerInventoryChange(args.player(), PlayerInventoryTransaction.EventCreator.STANDARD); + try (EffectTransactor ignored = BroadcastInventoryChangesEffect.transact(pipeline.transactor())) { + args.player().containerMenu.broadcastChanges(); + } + return new EffectResult<>(result, true); + } + +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InventoryEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InventoryEffect.java index 459b9c9c038..e58f1cb2849 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InventoryEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/InventoryEffect.java @@ -25,11 +25,11 @@ package org.spongepowered.common.event.tracking.context.transaction.effect; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class InventoryEffect implements ProcessingSideEffect { +public final class InventoryEffect implements ProcessingSideEffect { private static final class Holder { static final InventoryEffect INSTANCE = new InventoryEffect(); @@ -40,10 +40,10 @@ public static InventoryEffect getInstance() { InventoryEffect() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, - final BlockState newState, final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { - return EffectResult.NULL_RETURN; + return EffectResult.nullReturn(); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/NotifyClientEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/NotifyClientEffect.java index 63f49fc4df6..5ce7d8ec413 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/NotifyClientEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/NotifyClientEffect.java @@ -28,11 +28,12 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class NotifyClientEffect implements ProcessingSideEffect { +public final class NotifyClientEffect implements ProcessingSideEffect { private static final class Holder { static final NotifyClientEffect INSTANCE = new NotifyClientEffect(); @@ -45,9 +46,11 @@ public static NotifyClientEffect getInstance() { NotifyClientEffect() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, - final BlockState newState, final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { + final BlockState newState = args.newState(); + final SpongeBlockChangeFlag flag = args.flag(); final LevelChunk chunk = pipeline.getAffectedChunk(); final ServerLevel world = pipeline.getServerWorld(); @@ -56,9 +59,9 @@ public EffectResult processSideEffect(final BlockPipeline pipeline, final Pipeli // if ((flags & 2) != 0 && (!this.isClientSide || (flags & 4) == 0) && (this.isClientSide || chunk.getLocationType() != null && chunk.getLocationType().isAtLeast(ChunkHolder.LocationType.TICKING))) { if (flag.notifyClients() && (chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING))) { // this.notifyBlockUpdate(pos, blockstate, newWorldState, flags); - world.sendBlockUpdated(oldState.pos, oldState.state, newState, flag.getRawFlag()); + world.sendBlockUpdated(oldState.pos(), oldState.state(), newState, flag.getRawFlag()); } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/NotifyNeighborSideEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/NotifyNeighborSideEffect.java index 8702afdf483..7ea4cdd992e 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/NotifyNeighborSideEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/NotifyNeighborSideEffect.java @@ -26,11 +26,11 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class NotifyNeighborSideEffect implements ProcessingSideEffect { +public final class NotifyNeighborSideEffect implements ProcessingSideEffect { private static final class Holder { static final NotifyNeighborSideEffect INSTANCE = new NotifyNeighborSideEffect(); @@ -44,22 +44,29 @@ public static NotifyNeighborSideEffect getInstance() { @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { + final var flag = args.flag(); + final var newState = args.newState(); final ServerLevel world = pipeline.getServerWorld(); // Vanilla isClientSide is redundant - // if (!this.isClientSide && (flags & 1) != 0) { + // if (($$2 & 1) != 0) { + // this.updateNeighborsAt($$0, $$6.getBlock()); + // if (!this.isClientSide && $$1.hasAnalogOutputSignal()) { + // this.updateNeighbourForOutputSignal($$0, $$5); + // } + //} if (flag.updateNeighbors()) { // this.notifyNeighbors(pos, originalState.getBlock()); - world.blockUpdated(oldState.pos, oldState.state.getBlock()); + world.updateNeighborsAt(oldState.pos(), oldState.state().getBlock()); if (newState.hasAnalogOutputSignal()) { // this.updateComparatorOutputLevel(pos, block); - world.updateNeighbourForOutputSignal(oldState.pos, newState.getBlock()); + world.updateNeighbourForOutputSignal(oldState.pos(), newState.getBlock()); } } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/OldBlockOnReplaceEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/OldBlockOnReplaceEffect.java index 2c5584c3615..52ab4d58975 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/OldBlockOnReplaceEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/OldBlockOnReplaceEffect.java @@ -24,12 +24,13 @@ */ package org.spongepowered.common.event.tracking.context.transaction.effect; +import net.minecraft.world.level.block.BaseRailBlock; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class OldBlockOnReplaceEffect implements ProcessingSideEffect { +public final class OldBlockOnReplaceEffect implements ProcessingSideEffect { private static final class Holder { static final OldBlockOnReplaceEffect INSTANCE = new OldBlockOnReplaceEffect(); } @@ -42,25 +43,55 @@ public static OldBlockOnReplaceEffect getInstance() { } @Override - public EffectResult processSideEffect( - final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { - // Normally, vanilla does this: - // boolean var14 = var11.hasBlockEntity(); - // if (!this.level.isClientSide) { - // var11.onRemove(this.level, var1, var2, var3); - // } else if (!var11.is(var12) && var14) { - // this.removeBlockEntity(var1); - // } - // However, since we know we're not on the client (ChunkPipeline is not - // used outside of server world context) - // we can safely just do oldState.onRemove(this.level, var1, var2, var3). - if (flag.performBlockDestruction()) { - oldState.state.onRemove(pipeline.getServerWorld(), oldState.pos, newState, flag.movingBlocks()); - } else if (oldState.state.hasBlockEntity() && !oldState.state.is(newState.getBlock())) { - pipeline.getServerWorld().removeBlockEntity(oldState.pos); + // This replaces LevelChunk#setBlockState block: + /* + boolean $$13 = !$$9.is($$10); + boolean $$14 = ($$2 & 64) != 0; + boolean $$15 = ($$2 & 256) == 0; + if ($$13 && $$9.hasBlockEntity()) { + if (!this.level.isClientSide && $$15) { + BlockEntity $$16 = this.level.getBlockEntity($$0); + if ($$16 != null) { + $$16.preRemoveSideEffects($$0, $$9); + } + } + + this.removeBlockEntity($$0); + } + + if (($$13 || $$10 instanceof BaseRailBlock) && this.level instanceof ServerLevel $$17 && (($$2 & 1) != 0 || $$14)) { + $$9.affectNeighborsAfterRemoval($$17, $$0, $$14); } - return EffectResult.NULL_PASS; + Notes: + Since we know we're on the server context, we eliminate + if (this.level instanceof ServerLevel $$17) { + And likewise the check if hasBlockEntity, so we just fast forward into + the checks for physics flags + */ + boolean hasBlockEntity = oldState.state().hasBlockEntity(); + final var newState = args.newState(); + final var flag = args.flag(); + final var pos = oldState.pos(); + + final boolean replaced = !oldState.state().is(newState.getBlock()); + if (replaced && hasBlockEntity) { + if (flag.performBlockDestruction()) { + final var blockEntity = oldState.tileEntity(); + if (blockEntity != null) { + blockEntity.preRemoveSideEffects(pos, oldState.state()); + } + } + + pipeline.getAffectedChunk().removeBlockEntity(pos); + } + + if ((replaced || newState.getBlock() instanceof BaseRailBlock) && (flag.updateNeighbors() || flag.movingBlocks())) { + oldState.state().affectNeighborsAfterRemoval(pipeline.getServerWorld(), pos, flag.movingBlocks()); + } + + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/PerformBlockDropsFromDestruction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/PerformBlockDropsFromDestruction.java index 0e6a2b9d443..94896c4e5af 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/PerformBlockDropsFromDestruction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/PerformBlockDropsFromDestruction.java @@ -27,11 +27,11 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class PerformBlockDropsFromDestruction implements ProcessingSideEffect { +public final class PerformBlockDropsFromDestruction implements ProcessingSideEffect { private static final class Holder { static final PerformBlockDropsFromDestruction INSTANCE = new PerformBlockDropsFromDestruction(); @@ -43,11 +43,11 @@ public static PerformBlockDropsFromDestruction getInstance() { PerformBlockDropsFromDestruction() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, - final BlockState newState, final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { - Block.dropResources(oldState.state, pipeline.getServerWorld(), oldState.pos, oldState.tileEntity, oldState.destroyer, ItemStack.EMPTY); - return EffectResult.NULL_PASS; + Block.dropResources(oldState.state(), pipeline.getServerWorld(), oldState.pos(), oldState.tileEntity(), oldState.destroyer(), ItemStack.EMPTY); + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/PrepareBlockDrops.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/PrepareBlockDrops.java index 9c3c772729e..4ea2225adf3 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/PrepareBlockDrops.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/PrepareBlockDrops.java @@ -25,11 +25,11 @@ package org.spongepowered.common.event.tracking.context.transaction.effect; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class PrepareBlockDrops implements ProcessingSideEffect { +public final class PrepareBlockDrops implements ProcessingSideEffect { private static final class Holder { static final PrepareBlockDrops INSTANCE = new PrepareBlockDrops(); @@ -41,10 +41,10 @@ public static PrepareBlockDrops getInstance() { PrepareBlockDrops() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, - final BlockState newState, final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ProcessingSideEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ProcessingSideEffect.java index 2d62b184f61..be0e3f21018 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ProcessingSideEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/ProcessingSideEffect.java @@ -24,16 +24,17 @@ */ package org.spongepowered.common.event.tracking.context.transaction.effect; -import net.minecraft.world.level.block.state.BlockState; -import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; -import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; +import org.checkerframework.checker.nullness.qual.Nullable; @FunctionalInterface -public interface ProcessingSideEffect { +public interface ProcessingSideEffect { - EffectResult processSideEffect(BlockPipeline pipeline, PipelineCursor oldState, BlockState newState, SpongeBlockChangeFlag flag, - int limit - ); + interface Simple extends ProcessingSideEffect { + + } + + EffectResult processSideEffect(T pipeline, C oldState, A args); + + sealed interface Args permits BlockChangeArgs, InteractionAtArgs, UseItemOnArgs, UseItemAtArgs, UseItemArgs {} } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/RemoveTileEntityFromChunkEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/RemoveTileEntityFromChunkEffect.java index cc118789df8..77cf679a028 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/RemoveTileEntityFromChunkEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/RemoveTileEntityFromChunkEffect.java @@ -26,11 +26,11 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class RemoveTileEntityFromChunkEffect implements ProcessingSideEffect { +public final class RemoveTileEntityFromChunkEffect implements ProcessingSideEffect { private static final class Holder { static final RemoveTileEntityFromChunkEffect INSTANCE = new RemoveTileEntityFromChunkEffect(); @@ -43,14 +43,14 @@ public static RemoveTileEntityFromChunkEffect getInstance() { RemoveTileEntityFromChunkEffect() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { - final BlockEntity tileEntity = oldState.tileEntity; + final BlockEntity tileEntity = oldState.tileEntity(); if (tileEntity == null) { - return EffectResult.NULL_RETURN; + return EffectResult.nullReturn(); } - pipeline.getAffectedChunk().removeBlockEntity(oldState.pos); - return EffectResult.NULL_RETURN; + pipeline.getAffectedChunk().removeBlockEntity(oldState.pos()); + return EffectResult.nullReturn(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SetAndRegisterBlockEntityToLevelChunk.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SetAndRegisterBlockEntityToLevelChunk.java index 7733123bc27..4333e3e40d0 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SetAndRegisterBlockEntityToLevelChunk.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SetAndRegisterBlockEntityToLevelChunk.java @@ -31,9 +31,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class SetAndRegisterBlockEntityToLevelChunk implements ProcessingSideEffect { +public final class SetAndRegisterBlockEntityToLevelChunk implements ProcessingSideEffect { private static final class Holder { static final SetAndRegisterBlockEntityToLevelChunk INSTANCE = new SetAndRegisterBlockEntityToLevelChunk(); @@ -46,17 +45,16 @@ public static SetAndRegisterBlockEntityToLevelChunk getInstance() { SetAndRegisterBlockEntityToLevelChunk() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, - final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { final ServerLevel serverWorld = pipeline.getServerWorld(); - final @Nullable BlockEntity blockEntity = oldState.tileEntity; - final BlockPos pos = oldState.pos; + final @Nullable BlockEntity blockEntity = oldState.tileEntity(); + final BlockPos pos = oldState.pos(); if (serverWorld.isOutsideBuildHeight(pos)) { - return EffectResult.NULL_RETURN; + return EffectResult.nullReturn(); } pipeline.getAffectedChunk().addAndRegisterBlockEntity(blockEntity); - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SetBlockToChunkSectionEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SetBlockToChunkSectionEffect.java index 6db0b84c8fa..a680290bb94 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SetBlockToChunkSectionEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SetBlockToChunkSectionEffect.java @@ -26,11 +26,11 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunkSection; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class SetBlockToChunkSectionEffect implements ProcessingSideEffect { +public final class SetBlockToChunkSectionEffect implements ProcessingSideEffect { private static final class Holder { private static final SetBlockToChunkSectionEffect INSTANCE = new SetBlockToChunkSectionEffect(); @@ -42,17 +42,17 @@ public static SetBlockToChunkSectionEffect getInstance() { } @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { + final BlockState newState = args.newState(); final LevelChunkSection chunkSection = pipeline.getAffectedSection(); - final int x = oldState.pos.getX() & 15; - final int y = oldState.pos.getY() & 15; - final int z = oldState.pos.getZ() & 15; + final int x = oldState.pos().getX() & 15; + final int y = oldState.pos().getY() & 15; + final int z = oldState.pos().getZ() & 15; final BlockState oldStateReturned = chunkSection.setBlockState(x, y, z, newState); if (oldStateReturned == newState) { - return EffectResult.NULL_RETURN; + return EffectResult.nullReturn(); } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SpawnDestructBlocksEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SpawnDestructBlocksEffect.java index b167636ec06..ac4eeacd846 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SpawnDestructBlocksEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/SpawnDestructBlocksEffect.java @@ -29,13 +29,13 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; import java.util.List; -public final class SpawnDestructBlocksEffect implements ProcessingSideEffect { +public final class SpawnDestructBlocksEffect implements ProcessingSideEffect { private static final class Holder { static final SpawnDestructBlocksEffect INSTANCE = new SpawnDestructBlocksEffect(); @@ -47,19 +47,17 @@ public static SpawnDestructBlocksEffect getInstance() { SpawnDestructBlocksEffect() {} - @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public EffectResult processSideEffect( - final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, final SpongeBlockChangeFlag flag, - final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { final ServerLevel world = pipeline.getServerWorld(); - final BlockPos pos = oldState.pos; + final BlockPos pos = oldState.pos(); - final List drops = oldState.drops; + final List drops = oldState.drops(); drops.forEach(drop -> Block.popResource(world, pos, drop)); - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateChunkLightManagerEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateChunkLightManagerEffect.java index b27cfad1ec7..4117eb9ee03 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateChunkLightManagerEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateChunkLightManagerEffect.java @@ -24,17 +24,19 @@ */ package org.spongepowered.common.event.tracking.context.transaction.effect; +import net.minecraft.core.SectionPos; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunkSection; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class UpdateChunkLightManagerEffect implements ProcessingSideEffect { +public final class UpdateChunkLightManagerEffect implements ProcessingSideEffect { private static final class Holder { static final UpdateChunkLightManagerEffect INSTANCE = new UpdateChunkLightManagerEffect(); } + private UpdateChunkLightManagerEffect() { } @@ -43,16 +45,32 @@ public static UpdateChunkLightManagerEffect getInstance() { } @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, - final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { + /* + Continuing from LevelChunk.setBlockState + + boolean $$11 = $$4.hasOnlyAir(); + if ($$5 != $$11) { + this.level.getChunkSource().getLightEngine().updateSectionStatus($$0, $$11); + this.level.getChunkSource().onSectionEmptinessChanged(this.chunkPos.x, SectionPos.blockToSectionCoord($$3), this.chunkPos.z, $$11); + } + + */ + final var chunk = pipeline.getAffectedChunk(); + final var blockY = oldState.pos().getY(); final LevelChunkSection chunkSection = pipeline.getAffectedSection(); final boolean wasEmpty = pipeline.wasEmpty(); final boolean isStillEmpty = chunkSection.hasOnlyAir(); if (wasEmpty != isStillEmpty) { - pipeline.getServerWorld().getChunkSource().getLightEngine().updateSectionStatus(oldState.pos, isStillEmpty); + final var source = pipeline.getServerWorld().getChunkSource(); + source.getLightEngine().updateSectionStatus(oldState.pos(), isStillEmpty); + final var sectionPos = SectionPos.blockToSectionCoord(blockY); + final var chunkX = chunk.getPos().x(); + final var chunkZ = chunk.getPos().z(); + source.onSectionEmptinessChanged(chunkX, sectionPos, chunkZ, isStillEmpty); } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateConnectingBlocksEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateConnectingBlocksEffect.java index ca520619017..127b1ef7444 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateConnectingBlocksEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateConnectingBlocksEffect.java @@ -27,11 +27,11 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class UpdateConnectingBlocksEffect implements ProcessingSideEffect { +public final class UpdateConnectingBlocksEffect implements ProcessingSideEffect { private static final class Holder { static final UpdateConnectingBlocksEffect INSTANCE = new UpdateConnectingBlocksEffect(); @@ -44,23 +44,26 @@ public static UpdateConnectingBlocksEffect getInstance() { UpdateConnectingBlocksEffect() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, - final BlockState newState, final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { final ServerLevel world = pipeline.getServerWorld(); - final BlockPos pos = oldState.pos; + final BlockPos pos = oldState.pos(); + final var flag = args.flag(); + final var newState = args.newState(); + final var limit = args.limit(); if (flag.updateNeighboringShapes() && limit > 0) { // int i = p_241211_3_ & -34; // Vanilla negates 34 to flip neighbor notification and and "state drops" final int withoutNeighborDropsAndNestedNeighborUpdates = flag.asNestedNeighborUpdates().getRawFlag(); // blockstate.updateIndirectNeighbourShapes(this, p_241211_1_, i, p_241211_4_ - 1); - oldState.state.updateIndirectNeighbourShapes(world, pos, withoutNeighborDropsAndNestedNeighborUpdates, limit - 1); + oldState.state().updateIndirectNeighbourShapes(world, pos, withoutNeighborDropsAndNestedNeighborUpdates, limit - 1); // p_241211_2_.updateNeighbourShapes(this, p_241211_1_, i, p_241211_4_ - 1); newState.updateNeighbourShapes(world, pos, withoutNeighborDropsAndNestedNeighborUpdates, limit - 1); // p_241211_2_.updateIndirectNeighbourShapes(this, p_241211_1_, i, p_241211_4_ - 1); newState.updateIndirectNeighbourShapes(world, pos, withoutNeighborDropsAndNestedNeighborUpdates, limit - 1); } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateHeightMapEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateHeightMapEffect.java index 00693ff8e4a..a9ffa8d929a 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateHeightMapEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateHeightMapEffect.java @@ -26,14 +26,14 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.Heightmap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.accessor.world.level.chunk.ChunkAccessAccessor; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; import java.util.Map; -public final class UpdateHeightMapEffect implements ProcessingSideEffect { +public final class UpdateHeightMapEffect implements ProcessingSideEffect { private static final class Holder { static final UpdateHeightMapEffect INSTANCE = new UpdateHeightMapEffect(); @@ -46,20 +46,21 @@ public static UpdateHeightMapEffect getInstance() { } @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { + final var newState = args.newState(); final Map heightMap = ((ChunkAccessAccessor) pipeline.getAffectedChunk()).accessor$heightmaps(); if (heightMap == null) { throw new IllegalStateException("Heightmap dereferenced!"); } - final int x = oldState.pos.getX() & 15; - final int y = oldState.pos.getY(); - final int z = oldState.pos.getZ() & 15; + final int x = oldState.pos().getX() & 15; + final int y = oldState.pos().getY(); + final int z = oldState.pos().getZ() & 15; heightMap.get(Heightmap.Types.MOTION_BLOCKING).update(x, y, z, newState); heightMap.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES).update(x, y, z, newState); heightMap.get(Heightmap.Types.OCEAN_FLOOR).update(x, y, z, newState); heightMap.get(Heightmap.Types.WORLD_SURFACE).update(x, y, z, newState); - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateLightSideEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateLightSideEffect.java index c3f799992bb..62852b50b26 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateLightSideEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateLightSideEffect.java @@ -25,14 +25,15 @@ package org.spongepowered.common.event.tracking.context.transaction.effect; import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.lighting.LightEngine; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class UpdateLightSideEffect implements ProcessingSideEffect { +public final class UpdateLightSideEffect implements ProcessingSideEffect { private static final class Holder { static final UpdateLightSideEffect INSTANCE = new UpdateLightSideEffect(); @@ -46,17 +47,16 @@ public static UpdateLightSideEffect getInstance() { } @Override - public EffectResult processSideEffect( + public EffectResult<@Nullable BlockState> processSideEffect( final BlockPipeline pipeline, final PipelineCursor oldState, - final BlockState newState, final SpongeBlockChangeFlag flag, final int limit + final BlockChangeArgs args ) { + final var flag = args.flag(); if (!flag.updateLighting()) { - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } - final int originalOpactiy = oldState.opacity; final ServerLevel serverWorld = pipeline.getServerWorld(); - final LevelChunk chunk = pipeline.getAffectedChunk(); - final BlockState currentState = chunk.getBlockState(oldState.pos); + final BlockState currentState = pipeline.getAffectedChunk().getBlockState(oldState.pos()); // local variable notes: // var2 = oldState.state // var3 = currentState @@ -64,32 +64,34 @@ public EffectResult processSideEffect( // (param2 & 128) == 0 // this is handled above as flag.updateLighting() // && var3 != var2 // && ( - // var3.getLightBlock(this, param0) != var2.getLightBlock(this, param0) + // var3.getLightDampening() != var2.getLightDampening() // || var3.getLightEmission() != var2.getLightEmission() // || var3.useShapeForLightOcclusion() // || var2.useShapeForLightOcclusion() // ) // ) { - if (oldState.state != currentState - && (currentState.getLightBlock(serverWorld, oldState.pos) != originalOpactiy - || currentState.getLightEmission() != oldState.state.getLightEmission() - || currentState.useShapeForLightOcclusion() - || oldState.state.useShapeForLightOcclusion() - )) { - final int x = oldState.pos.getX() & 15; - final int y = oldState.pos.getY(); - final int z = oldState.pos.getZ() & 15; + if (LightEngine.hasDifferentLightProperties(oldState.state(), currentState)) { // ProfilerFiller $$12 = this.level.getProfiler(); - final ProfilerFiller profiler = serverWorld.getProfiler(); - profiler.push("updateSkyLightSources"); - // this.skyLightSources.update(this, x, y, z); - chunk.getSkyLightSources().update(chunk, x, y, z); - profiler.popPush("queueCheckLight"); - // this.level.getChunkProvider().getLightManager().checkBlock(pos); - serverWorld.getChunkSource().getLightEngine().checkBlock(oldState.pos); - profiler.pop(); + final ProfilerFiller filler = Profiler.get(); + + filler.push("updateSkyLightSources"); +// this.skyLightSources.update(this, $$6, $$3, $$8); + final var pos = oldState.pos(); + final var x = pos.getX() & 15; + final var y = pos.getY(); + final var z = pos.getZ() & 15; + + final var levelChunk = serverWorld.getChunk(pos); + levelChunk.getSkyLightSources().update(levelChunk, x, y, z); +// serverWorld.getChunkSource().update(this, $$6, $$3, $$8); + // this.profiler.startSection("queueCheckLight"); + filler.push("queueCheckLight"); + // this.getChunkProvider().getLightManager().checkBlock(pos); + serverWorld.getChunkSource().getLightEngine().checkBlock(pos); + // this.profiler.endSection(); + filler.pop(); } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateOrCreateNewTileEntityPostPlacementEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateOrCreateNewTileEntityPostPlacementEffect.java index 0ecc8ef372d..62b3d3cab8d 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateOrCreateNewTileEntityPostPlacementEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateOrCreateNewTileEntityPostPlacementEffect.java @@ -33,13 +33,13 @@ import org.spongepowered.common.bridge.world.level.block.state.BlockStateBridge; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class UpdateOrCreateNewTileEntityPostPlacementEffect implements ProcessingSideEffect { +public final class UpdateOrCreateNewTileEntityPostPlacementEffect implements ProcessingSideEffect { private static final class Holder { static final UpdateOrCreateNewTileEntityPostPlacementEffect INSTANCE = new UpdateOrCreateNewTileEntityPostPlacementEffect(); } + UpdateOrCreateNewTileEntityPostPlacementEffect() { } @@ -49,18 +49,28 @@ public static UpdateOrCreateNewTileEntityPostPlacementEffect getInstance() { @SuppressWarnings("deprecation") @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { + final var newState = args.newState(); + final var pos = oldState.pos(); final ServerLevel serverWorld = pipeline.getServerWorld(); final LevelChunk chunk = pipeline.getAffectedChunk(); + final var block = newState.getBlock(); if (((BlockStateBridge) newState).bridge$hasTileEntity()) { - final @Nullable BlockEntity maybeNewTileEntity = chunk.getBlockEntity(oldState.pos, LevelChunk.EntityCreationType.CHECK); + var maybeNewTileEntity = chunk.getBlockEntity(oldState.pos(), LevelChunk.EntityCreationType.CHECK); + if (maybeNewTileEntity != null && !maybeNewTileEntity.isValidBlockState(newState)) { + LevelChunkAccessor.accessor$LOGGER().warn( + "Found mismatched block entity @ {}: type = {}, state = {}", pos, maybeNewTileEntity.getType().builtInRegistryHolder().key().identifier(), newState + ); + chunk.removeBlockEntity(pos); + maybeNewTileEntity = null; + } if (maybeNewTileEntity == null) { // var15 = ((EntityBlock)var12).newBlockEntity(var1, var2); // Vanilla // tileentity1 = state.createTileEntity(this.world); // Forge // We cast to our bridge for easy access - @Nullable final BlockEntity newBlockEntity = ((BlockStateBridge) newState).bridge$createNewTileEntity(serverWorld, oldState.pos); + @Nullable final BlockEntity newBlockEntity = ((BlockStateBridge) newState).bridge$createNewTileEntity(serverWorld, oldState.pos()); if (newBlockEntity != null) { chunk.addAndRegisterBlockEntity(newBlockEntity); } @@ -69,6 +79,6 @@ public EffectResult processSideEffect(final BlockPipeline pipeline, final Pipeli ((LevelChunkAccessor) chunk).accessor$updateBlockEntityTicker(maybeNewTileEntity); } } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateWorldRendererEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateWorldRendererEffect.java index 67608577360..6c91e24d4d6 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateWorldRendererEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UpdateWorldRendererEffect.java @@ -25,11 +25,11 @@ package org.spongepowered.common.event.tracking.context.transaction.effect; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class UpdateWorldRendererEffect implements ProcessingSideEffect { +public final class UpdateWorldRendererEffect implements ProcessingSideEffect { private static final class Holder { static final UpdateWorldRendererEffect INSTANCE = new UpdateWorldRendererEffect(); @@ -42,12 +42,13 @@ public static UpdateWorldRendererEffect getInstance() { UpdateWorldRendererEffect() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, - final BlockState newState, final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, + final BlockChangeArgs args ) { - if (oldState.state != newState) { - pipeline.getServerWorld().setBlocksDirty(oldState.pos, oldState.state, newState); + final var newState = args.newState(); + if (oldState.state() != newState) { + pipeline.getServerWorld().setBlocksDirty(oldState.pos(), oldState.state(), newState); } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemArgs.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemArgs.java new file mode 100644 index 00000000000..eeb1c6c3f2f --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemArgs.java @@ -0,0 +1,40 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.context.transaction.effect; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +public record UseItemArgs( + Level world, + ServerPlayer player, + InteractionHand hand, + ItemStack copiedStack, + ServerPlayerGameMode gameMode +) implements ProcessingSideEffect.Args { +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemAtArgs.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemAtArgs.java new file mode 100644 index 00000000000..ab8616303c3 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemAtArgs.java @@ -0,0 +1,41 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.context.transaction.effect; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; + +public record UseItemAtArgs( + Level world, + ServerPlayer player, + InteractionHand hand, + BlockHitResult result, + ItemStack copiedStack, + boolean creative +) implements ProcessingSideEffect.Args { +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemAtEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemAtEffect.java new file mode 100644 index 00000000000..5f1f031232f --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemAtEffect.java @@ -0,0 +1,67 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.context.transaction.effect; + +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.context.UseOnContext; +import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; +import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; +import org.spongepowered.common.event.tracking.context.transaction.pipeline.InteractionPipeline; + +public class UseItemAtEffect implements ProcessingSideEffect.Simple, UseItemAtArgs, InteractionResult> { + + private static final class Holder { + static final UseItemAtEffect INSTANCE = new UseItemAtEffect(); + } + + private UseItemAtEffect() { + } + + public static UseItemAtEffect getInstance() { + return UseItemAtEffect.Holder.INSTANCE; + } + + @Override + public EffectResult processSideEffect( + InteractionPipeline pipeline, InteractionResult oldState, UseItemAtArgs args + ) { + final var stack = args.copiedStack(); + final InteractionResult result; + final var context = new UseOnContext(args.player(), args.hand(), args.result()); + if (args.creative()) { + int count = stack.getCount(); + result = stack.useOn(context); + stack.setCount(count); + } else { + result = stack.useOn(context); + } + pipeline.transactor().logPlayerInventoryChange(args.player(), PlayerInventoryTransaction.EventCreator.STANDARD); + try (EffectTransactor ignored = BroadcastInventoryChangesEffect.transact(pipeline.transactor())) { + args.player().containerMenu.broadcastChanges(); + } + return new EffectResult<>(result, true); + } + +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemEffect.java new file mode 100644 index 00000000000..05062274d74 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemEffect.java @@ -0,0 +1,58 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.context.transaction.effect; + +import net.minecraft.world.InteractionResult; +import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; +import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; +import org.spongepowered.common.event.tracking.context.transaction.pipeline.InteractionPipeline; + +public class UseItemEffect implements ProcessingSideEffect.Simple, UseItemArgs, InteractionResult> { + + private static final class Holder { + static final UseItemEffect INSTANCE = new UseItemEffect(); + } + + private UseItemEffect() { + } + + public static UseItemEffect getInstance() { + return UseItemEffect.Holder.INSTANCE; + } + + @Override + public EffectResult processSideEffect( + InteractionPipeline pipeline, InteractionResult oldState, UseItemArgs args + ) { + final var gameMode = args.gameMode(); + final InteractionResult result = gameMode.useItem(args.player(), args.world(), args.copiedStack(), args.hand()); + pipeline.transactor().logPlayerInventoryChange(args.player(), PlayerInventoryTransaction.EventCreator.STANDARD); + try (EffectTransactor ignored = BroadcastInventoryChangesEffect.transact(pipeline.transactor())) { + args.player().containerMenu.broadcastChanges(); + } + return new EffectResult<>(result, true); + } + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/type/ViewableInventory_Builder_DummyStepMixin_ItemStackLike.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemOnArgs.java similarity index 72% rename from src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/type/ViewableInventory_Builder_DummyStepMixin_ItemStackLike.java rename to src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemOnArgs.java index 7044bb40170..72029fd5deb 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/type/ViewableInventory_Builder_DummyStepMixin_ItemStackLike.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/UseItemOnArgs.java @@ -22,16 +22,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory.type; +package org.spongepowered.common.event.tracking.context.transaction.effect; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.item.inventory.type.ViewableInventory; -import org.spongepowered.asm.mixin.Mixin; -@Mixin(value = ViewableInventory.Builder.DummyStep.class, remap = false) -public interface ViewableInventory_Builder_DummyStepMixin_ItemStackLike { - - default ViewableInventory.Builder.BuildingStep item(ItemStackSnapshot item) { - return ((ViewableInventory.Builder.DummyStep) this).item(item); - } +public record UseItemOnArgs( + UseOnContext context, + ItemStack itemStack, + ItemStackSnapshot itemStackSnapshot, + ServerPlayer player, + boolean isCreative +) implements ProcessingSideEffect.Args { } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/WorldBlockChangeCompleteEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/WorldBlockChangeCompleteEffect.java index 686dea0bbe0..5f527883e4a 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/WorldBlockChangeCompleteEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/WorldBlockChangeCompleteEffect.java @@ -25,11 +25,12 @@ package org.spongepowered.common.event.tracking.context.transaction.effect; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class WorldBlockChangeCompleteEffect implements ProcessingSideEffect{ +public final class WorldBlockChangeCompleteEffect implements ProcessingSideEffect { private static final class Holder { static final WorldBlockChangeCompleteEffect INSTANCE = new WorldBlockChangeCompleteEffect(); @@ -42,14 +43,13 @@ public static WorldBlockChangeCompleteEffect getInstance() { WorldBlockChangeCompleteEffect() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { - final var world = pipeline.getServerWorld(); - final var nowState = pipeline.getAffectedChunk().getBlockState(oldState.pos); - if (nowState == newState && flag.notifyPathfinding()) { - world.onBlockStateChange(oldState.pos, oldState.state, nowState); + final BlockState newState = args.newState(); + final SpongeBlockChangeFlag flag = args.flag(); + if (flag.notifyPathfinding()) { + pipeline.getServerWorld().updatePOIOnBlockStateChange(oldState.pos(), oldState.state(), newState); } - return new EffectResult(oldState.state, true); + return new EffectResult<>(oldState.state(), true); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/WorldDestroyBlockLevelEffect.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/WorldDestroyBlockLevelEffect.java index d1dafaa2841..05a171680cd 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/WorldDestroyBlockLevelEffect.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/effect/WorldDestroyBlockLevelEffect.java @@ -27,11 +27,11 @@ import net.minecraft.world.level.block.BaseFireBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.context.transaction.pipeline.BlockPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; -import org.spongepowered.common.world.SpongeBlockChangeFlag; -public final class WorldDestroyBlockLevelEffect implements ProcessingSideEffect { +public final class WorldDestroyBlockLevelEffect implements ProcessingSideEffect { private static final class Holder { static final WorldDestroyBlockLevelEffect INSTANCE = new WorldDestroyBlockLevelEffect(); @@ -44,12 +44,12 @@ public static WorldDestroyBlockLevelEffect getInstance() { WorldDestroyBlockLevelEffect() {} @Override - public EffectResult processSideEffect(final BlockPipeline pipeline, final PipelineCursor oldState, final BlockState newState, - final SpongeBlockChangeFlag flag, final int limit + public EffectResult<@Nullable BlockState> processSideEffect( + final BlockPipeline pipeline, final PipelineCursor oldState, final BlockChangeArgs args ) { - if (!(oldState.state.getBlock() instanceof BaseFireBlock)) { - pipeline.getServerWorld().levelEvent(2001, oldState.pos, Block.getId(oldState.state)); + if (!(oldState.state().getBlock() instanceof BaseFireBlock)) { + pipeline.getServerWorld().levelEvent(2001, oldState.pos(), Block.getId(oldState.state())); } - return EffectResult.NULL_PASS; + return EffectResult.nullPass(); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ClickCreativeMenuTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ClickCreativeMenuTransaction.java index 281f3025672..64a92cae7e4 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ClickCreativeMenuTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ClickCreativeMenuTransaction.java @@ -107,10 +107,10 @@ public void postProcessEvent(PhaseContext<@NonNull ?> context, ClickContainerEve @Override boolean isContainerEventAllowed(final PhaseContext<@Nullable ?> context) { - if (!(context instanceof InventoryPacketContext)) { + if (!(context instanceof InventoryPacketContext ipc)) { return false; } - final int containerId = ((InventoryPacketContext) context).getPacket().getContainerId(); + final int containerId = ipc.getPacket().containerId(); return containerId != this.player.containerMenu.containerId; } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ClickMenuTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ClickMenuTransaction.java index 1100799f243..46e5f9dfbc9 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ClickMenuTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ClickMenuTransaction.java @@ -25,10 +25,12 @@ package org.spongepowered.common.event.tracking.context.transaction.inventory; import net.minecraft.network.protocol.game.ServerboundContainerClickPacket; +import net.minecraft.network.protocol.game.ServerboundPickItemFromBlockPacket; +import net.minecraft.network.protocol.game.ServerboundPickItemFromEntityPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.ContainerInput; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Transaction; @@ -51,13 +53,13 @@ public class ClickMenuTransaction extends ContainerBasedTransaction { private final ServerPlayer player; private final int slotNum; private final int buttonNum; - private final ClickType clickType; + private final ContainerInput clickType; private final @Nullable Slot slot; private final ItemStackSnapshot cursor; public ClickMenuTransaction( final Player player, final AbstractContainerMenu menu, final int slotNum, final int buttonNum, - final ClickType clickType, + final ContainerInput clickType, final @Nullable Slot slot, final ItemStackSnapshot cursor ) { @@ -106,10 +108,14 @@ public void postProcessEvent(final PhaseContext<@NonNull ?> context, final Click boolean isContainerEventAllowed( final PhaseContext<@NonNull ?> context ) { - if (!(context instanceof InventoryPacketContext)) { + if (!(context instanceof InventoryPacketContext ipc)) { return false; } - final int containerId = ((InventoryPacketContext) context).getPacket().getContainerId(); + if(ipc.getPacket() instanceof ServerboundPickItemFromBlockPacket || + ipc.getPacket() instanceof ServerboundPickItemFromEntityPacket) { + return true; //The item is always put in the player container menu + } + final int containerId = ipc.getPacket().containerId(); return containerId != this.player.containerMenu.containerId; } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/CloseMenuTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/CloseMenuTransaction.java index 984ef319772..dcc849187fc 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/CloseMenuTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/CloseMenuTransaction.java @@ -25,14 +25,8 @@ package org.spongepowered.common.event.tracking.context.transaction.inventory; import com.google.common.collect.ImmutableList; -import net.minecraft.network.protocol.game.ClientboundContainerClosePacket; -import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.InventoryMenu; -import net.minecraft.world.inventory.Slot; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -40,7 +34,6 @@ import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.item.inventory.ChangeInventoryEvent; import org.spongepowered.api.event.item.inventory.container.InteractContainerEvent; import org.spongepowered.api.item.inventory.Container; import org.spongepowered.api.item.inventory.ItemStackSnapshot; @@ -58,18 +51,16 @@ import java.util.Optional; import java.util.function.BiConsumer; -public class CloseMenuTransaction extends MenuBasedTransaction { +public class CloseMenuTransaction extends MenuBasedTransaction { private final ServerPlayer player; private final ItemStackSnapshot cursor; - private boolean clientSource; private @MonotonicNonNull List slotTransactions; - public CloseMenuTransaction(final Player player, final boolean clientSource) { + public CloseMenuTransaction(final Player player) { super(TransactionTypes.INTERACT_CONTAINER_EVENT.get(), player.containerMenu); this.player = (ServerPlayer) player; this.cursor = ItemStackUtil.snapshotOf(player.containerMenu.getCarried()); - this.clientSource = clientSource; } @Override @@ -80,31 +71,21 @@ public CloseMenuTransaction(final Player player, final boolean clientSource) { } @Override - public Optional generateEvent( + public Optional generateEvent( final PhaseContext<@NonNull ?> context, @Nullable final GameTransaction<@NonNull ?> parent, - final ImmutableList> gameTransactions, final Cause currentCause + final ImmutableList> gameTransactions, final Cause currentCause ) { - // TODO: API-14 post events? - if (true) { - return Optional.empty(); - } final ItemStackSnapshot resultingCursor = ItemStackUtil.snapshotOf(this.player.containerMenu.getCarried()); final Transaction cursorTransaction = new Transaction<>(this.cursor, resultingCursor); - final InteractContainerEvent.Close event = SpongeEventFactory.createInteractContainerEventClose(currentCause, (Container) this.menu, + final InteractContainerEvent.Close.Post event = SpongeEventFactory.createInteractContainerEventClosePost(currentCause, (Container) this.menu, cursorTransaction, (Container) this.menu, this.slotTransactions == null ? Collections.emptyList() : this.slotTransactions); return Optional.of(event); } @Override - public void restore(final PhaseContext<@NonNull ?> context, final InteractContainerEvent event) { - if (this.clientSource) { - // If client closed container we need to reopen it - this.reopen(this.player, this.menu); - } + public void restore(final PhaseContext<@NonNull ?> context, final InteractContainerEvent.Close.Post event) { PacketPhaseUtil.handleCursorRestore(this.player, event.cursorTransaction(), event.isCancelled()); - if (event instanceof ChangeInventoryEvent) { - PacketPhaseUtil.handleSlotRestore(this.player, this.menu, ((ChangeInventoryEvent) event).transactions(), event.isCancelled()); - } + PacketPhaseUtil.handleSlotRestore(this.player, this.menu, event.transactions(), event.isCancelled()); this.player.inventoryMenu.broadcastChanges(); // prevent double capture } @@ -124,26 +105,17 @@ public boolean absorbSlotTransaction( } @Override - public void postProcessEvent(final PhaseContext<@NonNull ?> context, final InteractContainerEvent event) { - if (!this.clientSource) { - // Server closed send packet to client - this.player.connection.send(new ClientboundContainerClosePacket(this.player.containerMenu.containerId)); - } - // Finish closing container - this.player.containerMenu = this.player.inventoryMenu; - + public void postProcessEvent(final PhaseContext<@NonNull ?> context, final InteractContainerEvent.Close.Post event) { // And restore cursor if needed PacketPhaseUtil.handleCursorRestore(this.player, event.cursorTransaction(), event.isCancelled()); - if (event instanceof ChangeInventoryEvent) { - PacketPhaseUtil.handleSlotRestore(this.player, this.menu, ((ChangeInventoryEvent) event).transactions(), event.isCancelled()); - } + PacketPhaseUtil.handleSlotRestore(this.player, this.menu, event.transactions(), event.isCancelled()); this.player.inventoryMenu.broadcastChanges(); // prevent double capture } @Override - public boolean markCancelledTransactions(final InteractContainerEvent event, - final ImmutableList> gameTransactions) { + public boolean markCancelledTransactions(final InteractContainerEvent.Close.Post event, + final ImmutableList> gameTransactions) { if (event.isCancelled()) { event.cursorTransaction().invalidate(); return true; @@ -155,37 +127,4 @@ public boolean markCancelledTransactions(final InteractContainerEvent event, public void addToPrinter(final PrettyPrinter printer) { } - - private void reopen(final ServerPlayer player, final AbstractContainerMenu container) { - if (container.getSlot(0) == null) { - return; - } - if (!(container instanceof InventoryMenu)) { - // Inventory closed by client, reopen window and send container - player.containerMenu = container; - final Slot slot = container.getSlot(0); - final net.minecraft.world.Container slotInventory = slot.container; - final net.minecraft.network.chat.@Nullable Component title; - // TODO get name from last open - if (slotInventory instanceof MenuProvider) { - title = ((MenuProvider) slotInventory).getDisplayName(); - } else { - // expected fallback for unknown types - title = null; - } - slotInventory.startOpen(player); - player.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), title)); - // resync data to client - container.broadcastFullState(); - } else { - // TODO: Maybe print a warning or throw an exception here? - // The player gui cannot be opened from the - // server so allowing this event to be cancellable when the - // GUI has been closed already would result - // in opening the wrong GUI window. - } - - } - - } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/CompositeTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/CompositeTransaction.java new file mode 100644 index 00000000000..6b7fe2aa6d0 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/CompositeTransaction.java @@ -0,0 +1,69 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.context.transaction.inventory; + +import com.google.common.collect.ImmutableList; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.CompositeEvent; +import org.spongepowered.api.event.Event; +import org.spongepowered.api.event.impl.AbstractCompositeEvent; +import org.spongepowered.common.event.tracking.context.transaction.GameTransaction; +import org.spongepowered.common.event.tracking.context.transaction.type.TransactionType; + +import java.util.stream.Stream; + +public abstract class CompositeTransaction> extends GameTransaction { + protected CompositeTransaction(TransactionType transactionType) { + super(transactionType); + } + + @Override + public void associateSideEffectEvents(E event, Stream elements) { + elements.forEach(event.children()::add); + } + + @Override + public void finalizeSideEffects(E post) { + // This finalizes the list to be immutable + ((AbstractCompositeEvent) post).postInit(); + } + + public void pushCause(CauseStackManager.StackFrame frame, E e) { + frame.pushCause(e.baseEvent()); + } + + @Override + public boolean markCancelledTransactions( + final E event, + final ImmutableList> gameTransactions) { + if (!event.isCancelled()) { + return false; + } + event.setCancelled(true); + gameTransactions.forEach(GameTransaction::markCancelled); + return true; + } + +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ContainerBasedTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ContainerBasedTransaction.java index 85298d6316f..99a164311f1 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ContainerBasedTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/ContainerBasedTransaction.java @@ -273,7 +273,7 @@ private void handleCrafting(final Player player, final ClickContainerEvent event SpongeEventFactory.createCraftItemEventCraft(PhaseTracker.getInstance().currentCause(), ContainerUtil.fromNative(this.menu), craftedItem, this.craftingInventory, event.cursorTransaction(), Optional.ofNullable(this.onTakeRecipe).map(r -> (CraftingRecipe) r.value()), - Optional.ofNullable(this.onTakeRecipe).map(r -> (ResourceKey) (Object) r.id()), + Optional.ofNullable(this.onTakeRecipe).map(r -> (ResourceKey) (Object) r.id().identifier()), Optional.of(this.craftingInventory.result()), event.transactions()); SpongeCommon.post(craftEvent); this.handleEventResults(player, craftEvent); @@ -284,13 +284,13 @@ private void handleCrafting(final Player player, final ClickContainerEvent event } private void handleCraftingPreview(final Player player, final ClickContainerEvent event) { - if (this.craftingInventory != null) { + if (this.craftingInventory != null && player instanceof ServerPlayer sp) { // TODO push event to cause? // TODO prevent event when there is no preview? final SlotTransaction previewTransaction = this.getPreviewTransaction(this.craftingInventory.result(), event.transactions()); - final var recipe = player.level().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, this.craftingContainer.asCraftInput(), player.level()); + final var recipe = sp.level().recipeAccess().getRecipeFor(RecipeType.CRAFTING, this.craftingContainer.asCraftInput(), player.level()); final CraftItemEvent.Preview previewEvent = SpongeEventFactory.createCraftItemEventPreview(event.cause(), (Container) this.menu, this.craftingInventory, event.cursorTransaction(), previewTransaction, - recipe.map(RecipeHolder::value).map(CraftingRecipe.class::cast), recipe.map(h -> h.id()).map(ResourceKey.class::cast), Optional.empty(), event.transactions()); + recipe.map(RecipeHolder::value).map(CraftingRecipe.class::cast), recipe.map(r -> (ResourceKey) (Object) r.id().identifier()), Optional.empty(), event.transactions()); SpongeCommon.post(previewEvent); this.handleEventResults(player, previewEvent); diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/CraftingPreviewTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/CraftingPreviewTransaction.java index 2fb3bf724e7..daec1f0974d 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/CraftingPreviewTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/CraftingPreviewTransaction.java @@ -71,16 +71,16 @@ public Optional parentAbsorber() { @Override Optional createInventoryEvent(final List slotTransactions, final List entities, final PhaseContext<@NonNull ?> context, final Cause currentCause) { - if (slotTransactions.isEmpty()) { + if (slotTransactions.isEmpty() || !(this.player instanceof ServerPlayer sp)) { return Optional.empty(); } final ItemStackSnapshot cursor = ItemStackUtil.snapshotOf(this.player.containerMenu.getCarried()); final SlotTransaction previewTransaction = this.getPreviewTransaction(this.craftingInventory.result(), slotTransactions); - final var recipe = this.player.level().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, this.craftSlots.asCraftInput(), this.player.level()); + final var recipe = sp.level().recipeAccess().getRecipeFor(RecipeType.CRAFTING, this.craftSlots.asCraftInput(), this.player.level()); final CraftItemEvent.Preview event = SpongeEventFactory.createCraftItemEventPreview(currentCause, ContainerUtil.fromNative(this.menu), this.craftingInventory, new Transaction<>(cursor, cursor), previewTransaction, recipe.map(RecipeHolder::value).map(CraftingRecipe.class::cast), - recipe.map(RecipeHolder::id).map(ResourceKey.class::cast), + recipe.map(r -> (ResourceKey) (Object) r.id().identifier()), Optional.empty(), slotTransactions); return Optional.of(event); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/InteractItemTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/InteractItemTransaction.java new file mode 100644 index 00000000000..c2a8fc0399c --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/InteractItemTransaction.java @@ -0,0 +1,96 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.context.transaction.inventory; + +import com.google.common.collect.ImmutableList; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.Event; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.item.inventory.InteractItemEvent; +import org.spongepowered.api.item.inventory.ItemStackSnapshot; +import org.spongepowered.common.event.tracking.PhaseContext; +import org.spongepowered.common.event.tracking.context.transaction.GameTransaction; +import org.spongepowered.common.event.tracking.context.transaction.type.TransactionTypes; +import org.spongepowered.common.item.util.ItemStackUtil; +import org.spongepowered.common.util.PrettyPrinter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.BiConsumer; + +public class InteractItemTransaction extends CompositeTransaction { + + private final ServerPlayer player; + private final ItemStackSnapshot stack; + + public InteractItemTransaction( + final ServerPlayer playerIn, final ItemStack stackIn) { + super(TransactionTypes.INTERACT_ITEM_SECONDARY.get()); + this.player = playerIn; + this.stack = ItemStackUtil.snapshotOf(stackIn); + + } + + @Override + public Optional, CauseStackManager.StackFrame>> getFrameMutator( + @Nullable GameTransaction<@NonNull ?> parent + ) { + return Optional.of((context, frame) -> { + frame.pushCause(this.player); + }); + } + + @Override + public void addToPrinter(PrettyPrinter printer) { + + } + + @Override + public Optional generateEvent( + final PhaseContext<@NonNull ?> context, + final @Nullable GameTransaction<@NonNull ?> parent, + final ImmutableList> gameTransactions, + final Cause currentCause + ) { + final var root = SpongeEventFactory.createInteractItemEventSecondary(currentCause, this.stack); + final List list = new ArrayList<>(); + final var composite = SpongeEventFactory.createInteractItemEventSecondaryPost(currentCause, root, list); + return Optional.of(composite); + } + + + + @Override + public void restore(PhaseContext<@NonNull ?> context, InteractItemEvent.Secondary.Post event) { + + } + +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/InteractItemWithBlockTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/InteractItemWithBlockTransaction.java new file mode 100644 index 00000000000..d49727d6adb --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/InteractItemWithBlockTransaction.java @@ -0,0 +1,112 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.context.transaction.inventory; + +import com.google.common.collect.ImmutableList; +import net.minecraft.server.level.ServerPlayer; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.block.BlockSnapshot; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.Event; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.block.InteractBlockEvent; +import org.spongepowered.api.util.Direction; +import org.spongepowered.api.util.Tristate; +import org.spongepowered.common.event.tracking.PhaseContext; +import org.spongepowered.common.event.tracking.context.transaction.GameTransaction; +import org.spongepowered.common.event.tracking.context.transaction.type.TransactionTypes; +import org.spongepowered.common.util.PrettyPrinter; +import org.spongepowered.math.vector.Vector3d; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.BiConsumer; + +public class InteractItemWithBlockTransaction extends CompositeTransaction { + + private final Vector3d hitVec; + private final BlockSnapshot snapshot; + private final Direction direction; + private final ServerPlayer player; + private final Tristate originalBlockResult, blockResult, originalItemResult, itemResult; + + public InteractItemWithBlockTransaction( + final ServerPlayer playerIn, final Vector3d hitVec, final BlockSnapshot snapshot, + final Direction direction, + final Tristate originalBlockResult, final Tristate useBlockResult, + final Tristate originalUseItemResult, final Tristate useItemResult) { + super(TransactionTypes.INTERACT_BLOCK_SECONDARY.get()); + this.player = playerIn; + this.hitVec = hitVec; + this.snapshot = snapshot; + this.direction = direction; + this.originalBlockResult = originalBlockResult; + this.blockResult = useBlockResult; + this.originalItemResult = originalUseItemResult; + this.itemResult = useItemResult; + } + + + @Override + public Optional, CauseStackManager.StackFrame>> getFrameMutator( + @Nullable GameTransaction<@NonNull ?> parent + ) { + return Optional.of((context, frame) -> { + frame.pushCause(this.player); + }); + } + + @Override + public void addToPrinter(PrettyPrinter printer) { + + } + + @Override + public Optional generateEvent( + final PhaseContext<@NonNull ?> context, + final @Nullable GameTransaction<@NonNull ?> parent, + final ImmutableList> gameTransactions, + final Cause currentCause + ) { + final var root = SpongeEventFactory.createInteractBlockEventSecondary(currentCause, + this.originalBlockResult, this.blockResult, + this.originalItemResult, this.itemResult, + this.snapshot, this.hitVec, + this.direction + ); + final List list = new ArrayList<>(); + final InteractBlockEvent.Secondary.Post composite = SpongeEventFactory.createInteractBlockEventSecondaryPost(currentCause, root, list); + return Optional.of(composite); + } + + @Override + public void restore(PhaseContext<@NonNull ?> context, InteractBlockEvent.Secondary.Post event) { + + } + +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/InventoryBasedTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/InventoryBasedTransaction.java index 45672accf3a..be736521434 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/InventoryBasedTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/InventoryBasedTransaction.java @@ -67,6 +67,11 @@ protected InventoryBasedTransaction(final Inventory inventory) { this.inventory = inventory; } + @Override + protected boolean hasUnknownChainRequiringCancellation() { + return true; + } + @Override public Optional, CauseStackManager.StackFrame>> getFrameMutator( @Nullable final GameTransaction<@NonNull ?> parent diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/PlaceCookingRecipeTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/PlaceCookingRecipeTransaction.java index ba484066b3f..306586d48c9 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/PlaceCookingRecipeTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/PlaceCookingRecipeTransaction.java @@ -82,7 +82,7 @@ Optional createInventoryEvent( (org.spongepowered.api.block.entity.BlockEntity) this.blockEntity, (Container) this.menu, cursorTransaction, Optional.empty(), Optional.of(this.recipe).map(RecipeHolder::value).map(CookingRecipe.class::cast), - Optional.of(this.recipe).map(RecipeHolder::id).map(ResourceKey.class::cast), + Optional.of(this.recipe).map(r -> (ResourceKey) (Object) r.id().identifier()), Optional.empty(), slotTransactions) ); } @@ -90,7 +90,7 @@ Optional createInventoryEvent( (org.spongepowered.api.block.entity.BlockEntity) this.blockEntity, (Container) this.menu, cursorTransaction, Optional.empty(), Optional.of(this.recipe).map(RecipeHolder::value).map(CookingRecipe.class::cast), - Optional.of(this.recipe).map(RecipeHolder::id).map(ResourceKey.class::cast), + Optional.of(this.recipe).map(r -> (ResourceKey) (Object) r.id().identifier()), Optional.empty(), slotTransactions); return Optional.of(event); } @@ -110,7 +110,7 @@ boolean isContainerEventAllowed(final PhaseContext<@Nullable ?> context) { if (!(context instanceof InventoryPacketContext)) { return false; } - final int containerId = ((InventoryPacketContext) context).getPacket().getContainerId(); + final int containerId = ((InventoryPacketContext) context).getPacket().containerId(); return containerId != this.player.containerMenu.containerId; } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/PlaceCraftingRecipeTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/PlaceCraftingRecipeTransaction.java index 22b9c2b8f94..6a24b30c9d8 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/PlaceCraftingRecipeTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/PlaceCraftingRecipeTransaction.java @@ -77,13 +77,13 @@ Optional createInventoryEvent( event = SpongeEventFactory.createClickContainerEventRecipeAll(cause, (Container) this.menu, this.craftingInventory, cursorTransaction, preview, Optional.of(this.recipe).map(RecipeHolder::value).map(CraftingRecipe.class::cast), - Optional.of(this.recipe).map(RecipeHolder::id).map(ResourceKey.class::cast), + Optional.of(this.recipe).map(r -> (ResourceKey) (Object) r.id().identifier()), Optional.empty(), slotTransactions); } else { event = SpongeEventFactory.createClickContainerEventRecipeSingle(cause, (Container) this.menu, this.craftingInventory, cursorTransaction, preview, Optional.of(this.recipe).map(RecipeHolder::value).map(CraftingRecipe.class::cast), - Optional.of(this.recipe).map(RecipeHolder::id).map(ResourceKey.class::cast), + Optional.of(this.recipe).map(r -> (ResourceKey) (Object) r.id().identifier()), Optional.empty(), slotTransactions); } return Optional.of(event); @@ -104,7 +104,7 @@ boolean isContainerEventAllowed(final PhaseContext<@Nullable ?> context) { if (!(context instanceof InventoryPacketContext)) { return false; } - final int containerId = ((InventoryPacketContext) context).getPacket().getContainerId(); + final int containerId = ((InventoryPacketContext) context).getPacket().containerId(); return containerId != this.player.containerMenu.containerId; } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/SelectTradeTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/SelectTradeTransaction.java index 8d5f9eaf98e..c5f6a2ad3c1 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/SelectTradeTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/SelectTradeTransaction.java @@ -91,7 +91,7 @@ boolean isContainerEventAllowed(final PhaseContext<@NonNull ?> context) { if (!(context instanceof InventoryPacketContext)) { return false; } - final int containerId = ((InventoryPacketContext) context).getPacket().getContainerId(); + final int containerId = ((InventoryPacketContext) context).getPacket().containerId(); return containerId != this.player.containerMenu.containerId; } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/SetCarriedItemTransaction.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/SetCarriedItemTransaction.java index 84a00d5bc79..9087d789e01 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/SetCarriedItemTransaction.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/SetCarriedItemTransaction.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.event.tracking.context.transaction.inventory; -import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket; +import net.minecraft.network.protocol.game.ClientboundSetHeldSlotPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import org.checkerframework.checker.nullness.qual.NonNull; @@ -54,7 +54,7 @@ public SetCarriedItemTransaction(final Player player, int newSlot) { super((Inventory) player.getInventory()); this.player = (ServerPlayer) player; final PlayerInventory inventory = (PlayerInventory) this.player.getInventory(); - this.prevSlotId = this.player.getInventory().selected; + this.prevSlotId = this.player.getInventory().getSelectedSlot(); this.prevSlot = inventory.hotbar().slot(this.prevSlotId).orElse(null); this.newSlot = inventory.hotbar().slot(newSlot).orElse(null); } @@ -76,8 +76,8 @@ Optional createInventoryEvent(final List @Override public void restore(final PhaseContext<@NonNull ?> context, final ChangeInventoryEvent event) { - this.player.connection.send(new ClientboundSetCarriedItemPacket(this.prevSlotId)); - this.player.getInventory().selected = this.prevSlotId; + this.player.connection.send(new ClientboundSetHeldSlotPacket(this.prevSlotId)); + this.player.getInventory().setSelectedSlot(this.prevSlotId); this.handleEventResults(this.player, event); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/ChunkPipeline.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/ChunkPipeline.java index a6b34239bc1..d5bfcfe180b 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/ChunkPipeline.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/ChunkPipeline.java @@ -37,6 +37,7 @@ import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; import org.spongepowered.common.event.tracking.context.transaction.ResultingTransactionBySideEffect; import org.spongepowered.common.event.tracking.context.transaction.block.ChangeBlock; +import org.spongepowered.common.event.tracking.context.transaction.effect.BlockChangeArgs; import org.spongepowered.common.event.tracking.context.transaction.effect.EffectResult; import org.spongepowered.common.event.tracking.context.transaction.effect.ProcessingSideEffect; import org.spongepowered.common.world.SpongeBlockChangeFlag; @@ -111,25 +112,24 @@ public LevelChunkSection getAffectedSection() { return null; } final ServerLevel serverWorld = this.serverWorld.get(); - final int oldOpacity = currentState.getLightBlock(serverWorld, pos); + final int oldOpacity = currentState.getLightDampening(); final SpongeBlockChangeFlag flag = this.transaction.getBlockChangeFlag(); final @Nullable BlockEntity existing = this.chunkSupplier.get().getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); - PipelineCursor formerState = new PipelineCursor(currentState, oldOpacity, pos, existing, (Entity) null, limit); + PipelineCursor formerState = new PipelineCursor(currentState, pos, existing, (Entity) null, limit); for (final ResultingTransactionBySideEffect effect : this.chunkEffects) { try (final EffectTransactor ignored = context.getTransactor().pushEffect(effect)) { - final EffectResult result = effect.effect.processSideEffect( + final var args = new BlockChangeArgs(proposedState, flag, limit); + final EffectResult<@Nullable BlockState> result = effect.effect.processSideEffect( this, formerState, - proposedState, - flag, - limit + args ); if (result.hasResult) { return result.resultingState; } - if (formerState.drops.isEmpty() && !result.drops.isEmpty()) { - formerState = new PipelineCursor(currentState, oldOpacity, pos, existing, null, result.drops, limit); + if (formerState.drops().isEmpty() && !result.drops.isEmpty()) { + formerState = new PipelineCursor(currentState, pos, existing, null, result.drops, limit); } } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/InteractionPipeline.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/InteractionPipeline.java new file mode 100644 index 00000000000..8de31066fd3 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/InteractionPipeline.java @@ -0,0 +1,56 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.context.transaction.pipeline; + +import net.minecraft.world.InteractionResult; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.common.event.tracking.PhaseContext; +import org.spongepowered.common.event.tracking.context.transaction.ResultingTransactionBySideEffect; +import org.spongepowered.common.event.tracking.context.transaction.TransactionalCaptureSupplier; +import org.spongepowered.common.event.tracking.context.transaction.effect.ProcessingSideEffect; + +import java.util.Objects; + +public record InteractionPipeline<@NonNull A extends ProcessingSideEffect.Args>( + A args, + InteractionResult defaultResult, + ProcessingSideEffect, InteractionResult, A, InteractionResult> effect, + TransactionalCaptureSupplier transactor +) { + + public InteractionResult processInteraction(final PhaseContext context) { + var result = this.defaultResult; + final ResultingTransactionBySideEffect, InteractionResult, A, InteractionResult> resultingEffect = new ResultingTransactionBySideEffect<>(this.effect); + try (final var ignored = context.getTransactor().pushEffect(resultingEffect)) { + final var effectResult = this.effect.processSideEffect(this, result, this.args); + if (effectResult.hasResult) { + final @Nullable InteractionResult resultingState = effectResult.resultingState; + result = Objects.requireNonNullElse(resultingState, result); + } + } + return result; + } +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/PipelineCursor.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/PipelineCursor.java index 6681d1e055c..4b3da670861 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/PipelineCursor.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/PipelineCursor.java @@ -33,57 +33,20 @@ import java.util.Collections; import java.util.List; -import java.util.StringJoiner; -public final class PipelineCursor { - public final BlockState state; - public final int opacity; - public final BlockPos pos; - public final @Nullable BlockEntity tileEntity; - public final @Nullable Entity destroyer; - public final List drops; - public final int limit; - - public PipelineCursor(final BlockState state, final int opacity, final BlockPos pos, - final @Nullable BlockEntity tileEntity, - final @Nullable Entity destroyer, final int limit +public record PipelineCursor( + BlockState state, + BlockPos pos, + @Nullable BlockEntity tileEntity, + @Nullable Entity destroyer, + List drops, + int limit +) { + public PipelineCursor(final BlockState state, final BlockPos pos, + final @Nullable BlockEntity tileEntity, + final @Nullable Entity destroyer, final int limit ) { - this.state = state; - this.opacity = opacity; - this.pos = pos; - this.tileEntity = tileEntity; - this.destroyer = destroyer; - this.drops = Collections.emptyList(); - this.limit = limit; + this(state, pos, tileEntity, destroyer, Collections.emptyList(), limit); } - public PipelineCursor(final BlockState state, final int opacity, final BlockPos pos, - final @Nullable BlockEntity tileEntity, - final @Nullable Entity destroyer, - final List drops, - final int limit - ) { - this.state = state; - this.opacity = opacity; - this.pos = pos; - this.tileEntity = tileEntity; - this.drops = drops; - this.limit = limit; - this.destroyer = destroyer; - } - - @Override - public String toString() { - return new StringJoiner( - ", ", - PipelineCursor.class.getSimpleName() + "[", - "]" - ) - .add("state=" + this.state) - .add("opacity=" + this.opacity) - .add("pos=" + this.pos) - .add("tileEntity=" + this.tileEntity) - .add("drops=" + this.drops) - .toString(); - } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/TileEntityPipeline.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/TileEntityPipeline.java index ac6fdd6ac4b..8f684044a29 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/TileEntityPipeline.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/TileEntityPipeline.java @@ -26,6 +26,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; import org.checkerframework.checker.nullness.qual.Nullable; @@ -33,6 +34,7 @@ import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; import org.spongepowered.common.event.tracking.context.transaction.ResultingTransactionBySideEffect; +import org.spongepowered.common.event.tracking.context.transaction.effect.BlockChangeArgs; import org.spongepowered.common.event.tracking.context.transaction.effect.EffectResult; import org.spongepowered.common.event.tracking.context.transaction.effect.ProcessingSideEffect; import org.spongepowered.common.world.SpongeBlockChangeFlag; @@ -49,7 +51,7 @@ public final class TileEntityPipeline implements BlockPipeline { private final @Nullable Supplier chunkSupplier; private final @Nullable Supplier serverWorld; private final @Nullable Supplier sectionSupplier; - private final List effects; + private final List> effects; private TileEntityPipeline(final Builder builder) { this.chunkSupplier = builder.chunkSupplier; @@ -98,23 +100,25 @@ public boolean wasEmpty() { public boolean processEffects(final PhaseContext context, final PipelineCursor initialCursor) { PipelineCursor currentCursor = initialCursor; - for (final ResultingTransactionBySideEffect effect : this.effects) { + for (final ResultingTransactionBySideEffect effect : this.effects) { try (final EffectTransactor ignored = context.getTransactor().pushEffect(effect)) { - final EffectResult result = effect.effect.processSideEffect( + final BlockChangeArgs args = new BlockChangeArgs( + currentCursor.state(), + (SpongeBlockChangeFlag) BlockChangeFlags.NONE, + currentCursor.limit() + ); + final EffectResult<@Nullable BlockState> result = effect.effect.processSideEffect( this, currentCursor, - currentCursor.state, - (SpongeBlockChangeFlag) BlockChangeFlags.NONE, - currentCursor.limit + args ); - if (result.resultingState != currentCursor.state) { + if (result.resultingState != currentCursor.state()) { currentCursor = new PipelineCursor( result.resultingState, - currentCursor.opacity, - currentCursor.pos, - currentCursor.tileEntity, - currentCursor.destroyer, - currentCursor.limit + currentCursor.pos(), + currentCursor.tileEntity(), + currentCursor.destroyer(), + currentCursor.limit() ); } if (result.hasResult) { @@ -130,13 +134,13 @@ public static final class Builder { @Nullable Supplier serverWorld; @Nullable Supplier chunkSupplier; @Nullable Supplier sectionSupplier; - List effects; + List> effects; - public Builder addEffect(final ProcessingSideEffect effect) { + public Builder addEffect(final ProcessingSideEffect effect) { if (this.effects == null) { this.effects = new LinkedList<>(); } - this.effects.add(new ResultingTransactionBySideEffect(Objects.requireNonNull(effect, "Effect is null"))); + this.effects.add(new ResultingTransactionBySideEffect<>(Objects.requireNonNull(effect, "Effect is null"))); return this; } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/WorldPipeline.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/WorldPipeline.java index 81165311770..93a067a988d 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/WorldPipeline.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/pipeline/WorldPipeline.java @@ -36,6 +36,7 @@ import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; import org.spongepowered.common.event.tracking.context.transaction.ResultingTransactionBySideEffect; +import org.spongepowered.common.event.tracking.context.transaction.effect.BlockChangeArgs; import org.spongepowered.common.event.tracking.context.transaction.effect.EffectResult; import org.spongepowered.common.event.tracking.context.transaction.effect.ProcessingSideEffect; import org.spongepowered.common.world.SpongeBlockChangeFlag; @@ -52,7 +53,7 @@ public final class WorldPipeline implements BlockPipeline { private final Supplier serverWorld; private final Supplier sectionSupplier; private final boolean wasEmpty; - private final List worldEffects; + private final List> worldEffects; private final ChunkPipeline chunkPipeline; WorldPipeline(final Builder builder) { @@ -98,23 +99,22 @@ public boolean processEffects(final PhaseContext context, final BlockState cu if (oldState == null) { return false; } - final int oldOpacity = oldState.getLightBlock(serverWorld, pos); - PipelineCursor formerState = new PipelineCursor(oldState, oldOpacity, pos, existing, destroyer, limit); + final int oldOpacity = oldState.getLightDampening(); + PipelineCursor formerState = new PipelineCursor(oldState, pos, existing, destroyer, limit); - for (final ResultingTransactionBySideEffect effect : this.worldEffects) { + for (final ResultingTransactionBySideEffect effect : this.worldEffects) { try (final EffectTransactor ignored = context.getTransactor().pushEffect(effect)) { - final EffectResult result = effect.effect.processSideEffect( + final var args = new BlockChangeArgs(newProposedState, flag, limit); + final EffectResult<@Nullable BlockState> result = effect.effect.processSideEffect( this, formerState, - newProposedState, - flag, - limit + args ); if (result.hasResult) { return result.resultingState != null; } - if (formerState.drops.isEmpty() && !result.drops.isEmpty()) { - formerState = new PipelineCursor(oldState, oldOpacity, pos, existing, formerState.destroyer, result.drops, limit); + if (formerState.drops().isEmpty() && !result.drops.isEmpty()) { + formerState = new PipelineCursor(oldState, pos, existing, formerState.destroyer(), result.drops, limit); } } } @@ -134,8 +134,8 @@ public static final class Builder { final Supplier serverWorld; final Supplier chunkSupplier; - final Supplier sectionSupplier; - @MonotonicNonNull List effects; + final Supplier<@Nullable LevelChunkSection> sectionSupplier; + @MonotonicNonNull List> effects; final ChunkPipeline chunkPipeline; Builder(final ChunkPipeline chunkPipeline) { @@ -145,11 +145,11 @@ public static final class Builder { this.chunkPipeline = chunkPipeline; } - public Builder addEffect(final ProcessingSideEffect effect) { + public Builder addEffect(final ProcessingSideEffect effect) { if (this.effects == null) { this.effects = new LinkedList<>(); } - this.effects.add(new ResultingTransactionBySideEffect(Objects.requireNonNull(effect, "Effect is null"))); + this.effects.add(new ResultingTransactionBySideEffect<>(Objects.requireNonNull(effect, "Effect is null"))); return this; } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/type/BlockTransactionType.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/type/BlockTransactionType.java index b8605f062fe..965ea025c85 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/type/BlockTransactionType.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/type/BlockTransactionType.java @@ -63,7 +63,7 @@ protected void consumeEventsAndMarker( eventsByWorld.asMap().forEach((key, events) -> { final Optional serverWorld = ((SpongeServer) SpongeCommon.server()).worldManager().world(key); - if (!serverWorld.isPresent()) { + if (serverWorld.isEmpty()) { return; } @@ -85,7 +85,7 @@ protected void consumeEventsAndMarker( final Operation operation = context.getBlockOperation(original, result); final BlockTransactionReceipt eventTransaction = new BlockTransactionReceipt(original, result, operation); transactions.add(eventTransaction); - context.postBlockTransactionApplication(original.blockChange, eventTransaction); + context.postBlockTransactionApplication(serverWorld.get(), original.blockChange, eventTransaction); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/type/TransactionTypes.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/type/TransactionTypes.java index 2301cc5bfb0..8569612f2dc 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/type/TransactionTypes.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/type/TransactionTypes.java @@ -29,12 +29,14 @@ import org.spongepowered.api.event.Cancellable; import org.spongepowered.api.event.Event; import org.spongepowered.api.event.block.ChangeBlockEvent; +import org.spongepowered.api.event.block.InteractBlockEvent; import org.spongepowered.api.event.block.NotifyNeighborBlockEvent; import org.spongepowered.api.event.block.ScheduleBlockUpdateEvent; import org.spongepowered.api.event.entity.HarvestEntityEvent; import org.spongepowered.api.event.entity.SpawnEntityEvent; import org.spongepowered.api.event.item.inventory.AffectSlotEvent; import org.spongepowered.api.event.item.inventory.ChangeInventoryEvent; +import org.spongepowered.api.event.item.inventory.InteractItemEvent; import org.spongepowered.api.event.item.inventory.container.ClickContainerEvent; import org.spongepowered.api.event.item.inventory.container.InteractContainerEvent; import org.spongepowered.api.registry.DefaultedRegistryReference; @@ -67,6 +69,8 @@ public final class TransactionTypes { public static final DefaultedRegistryReference> SLOT_CHANGE = TransactionTypes.key(ResourceKey.sponge("slot_change")); + public static final DefaultedRegistryReference> INTERACT_BLOCK_SECONDARY = TransactionTypes.key(ResourceKey.sponge("interact_block_secondary")); + public static final DefaultedRegistryReference> INTERACT_ITEM_SECONDARY = TransactionTypes.key(ResourceKey.sponge("interact_block_secondary")); public static final DefaultedRegistryReference> SPAWN_ENTITY = TransactionTypes.key(ResourceKey.sponge("spawn_entity")); // SORTFIELDS:OFF diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/entity/TeleportContext.java b/src/main/java/org/spongepowered/common/event/tracking/phase/entity/TeleportContext.java index 45a1d716016..89886e58fa6 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/entity/TeleportContext.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/entity/TeleportContext.java @@ -28,23 +28,12 @@ import org.spongepowered.common.event.tracking.PhaseTracker; public final class TeleportContext extends EntityContext { - - private boolean isPlayer; private boolean isWorldChange; public TeleportContext(final IPhaseState state, PhaseTracker tracker) { super(state, tracker); } - public boolean isPlayer() { - return this.isPlayer; - } - - public TeleportContext player() { - this.isPlayer = true; - return this; - } - public boolean isWorldChange() { return this.isWorldChange; } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/general/CommandState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/general/CommandState.java index d3d057b9d93..33d3b450f6d 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/general/CommandState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/general/CommandState.java @@ -35,6 +35,7 @@ import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.cause.entity.SpawnType; import org.spongepowered.api.event.cause.entity.SpawnTypes; +import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge; import org.spongepowered.common.entity.PlayerTracker; import org.spongepowered.common.event.tracking.PhaseTracker; @@ -67,7 +68,7 @@ public BiConsumer getFrameMod @Override public void postBlockTransactionApplication( - final CommandPhaseContext context, final BlockChange blockChange, + final CommandPhaseContext context, ServerWorld serverWorld, final BlockChange blockChange, final BlockTransactionReceipt transaction ) { // We want to investigate if there is a user on the cause stack @@ -97,7 +98,7 @@ public void unwind(final CommandPhaseContext phaseContext) { public Supplier attemptWorldKey(CommandPhaseContext context) { final Optional playerSource = context.getSource(net.minecraft.world.entity.player.Player.class); if (playerSource.isPresent()) { - return () -> (ResourceKey) (Object) playerSource.get().level().dimension().location(); + return () -> (ResourceKey) (Object) playerSource.get().level().dimension().identifier(); } return super.attemptWorldKey(context); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/general/ExplosionContext.java b/src/main/java/org/spongepowered/common/event/tracking/phase/general/ExplosionContext.java index 0c901223d8c..eb40f98eba9 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/general/ExplosionContext.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/general/ExplosionContext.java @@ -26,17 +26,17 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Explosion; +import net.minecraft.world.level.ServerExplosion; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.common.accessor.world.level.ExplosionAccessor; +import org.spongepowered.common.accessor.world.level.ServerExplosionAccessor; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.util.PrettyPrinter; public final class ExplosionContext extends GeneralPhaseContext { - private Explosion explosion; + private ServerExplosion explosion; public ExplosionContext(PhaseTracker tracker) { super(GeneralPhase.State.EXPLOSION, tracker); @@ -57,12 +57,12 @@ public ExplosionContext potentialExplosionSource(final ServerLevel worldServer, return this; } - public ExplosionContext explosion(final Explosion explosion) { + public ExplosionContext explosion(final ServerExplosion explosion) { this.explosion = explosion; return this; } - public Explosion getExplosion() { + public ServerExplosion getExplosion() { return this.explosion; } @@ -88,12 +88,10 @@ protected boolean isRunaway(final PhaseContext phaseContext) { if (phaseContext.getClass() != ExplosionContext.class) { return false; } - final ExplosionAccessor otherExplosion = (ExplosionAccessor) ((ExplosionContext) phaseContext).explosion; - final ExplosionAccessor thisExplosion = (ExplosionAccessor) this.explosion; + final ServerExplosionAccessor otherExplosion = (ServerExplosionAccessor) ((ExplosionContext) phaseContext).explosion; + final ServerExplosionAccessor thisExplosion = (ServerExplosionAccessor) this.explosion; return otherExplosion.accessor$level() == thisExplosion.accessor$level() - && otherExplosion.accessor$x() == thisExplosion.accessor$x() - && otherExplosion.accessor$y() == thisExplosion.accessor$y() - && otherExplosion.accessor$z() == thisExplosion.accessor$z(); + && ((ExplosionContext) phaseContext).explosion.center().equals(this.explosion.center()); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/general/ExplosionState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/general/ExplosionState.java index 33e19b5a729..e3553b4017b 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/general/ExplosionState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/general/ExplosionState.java @@ -37,7 +37,7 @@ import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.entity.SpawnEntityEvent; -import org.spongepowered.common.accessor.world.level.ExplosionAccessor; +import org.spongepowered.common.accessor.world.level.ServerExplosionAccessor; import org.spongepowered.common.bridge.CreatorTrackedBridge; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.event.tracking.TrackingUtil; @@ -85,8 +85,8 @@ public void populateLootContext(final ExplosionContext phaseContext, final LootP final Explosion explosion = phaseContext.getExplosion(); lootBuilder.withOptionalParameter(LootContextParams.THIS_ENTITY, explosion.getDirectSourceEntity()); - if (((ExplosionAccessor) explosion).accessor$blockInteraction() == net.minecraft.world.level.Explosion.BlockInteraction.DESTROY) { - lootBuilder.withParameter(LootContextParams.EXPLOSION_RADIUS, ((ExplosionAccessor) explosion).accessor$radius()); + if (((ServerExplosionAccessor) explosion).accessor$blockInteraction() == net.minecraft.world.level.Explosion.BlockInteraction.DESTROY) { + lootBuilder.withParameter(LootContextParams.EXPLOSION_RADIUS, ((ServerExplosionAccessor) explosion).accessor$radius()); } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/general/GeneralPhase.java b/src/main/java/org/spongepowered/common/event/tracking/phase/general/GeneralPhase.java index dc7277a207c..3f424b0d213 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/general/GeneralPhase.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/general/GeneralPhase.java @@ -35,6 +35,7 @@ public static final class State { public static final IPhaseState COMMAND = new CommandState(); public static final IPhaseState EXPLOSION = new ExplosionState(); public static final IPhaseState COMPLETE = new CompletePhase(); + public static final IPhaseState RECIPE_CRAFTING = new RecipeCraftingState(); private State() { } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/general/RecipeContext.java b/src/main/java/org/spongepowered/common/event/tracking/phase/general/RecipeContext.java new file mode 100644 index 00000000000..86ba8df7dd2 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/general/RecipeContext.java @@ -0,0 +1,156 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.phase.general; + + +import net.minecraft.core.BlockPos; +import net.minecraft.resources.Identifier; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.SingleItemRecipe; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.spongepowered.api.item.inventory.ItemStackSnapshot; +import org.spongepowered.api.world.BlockChangeFlags; +import org.spongepowered.common.block.SpongeBlockSnapshot; +import org.spongepowered.common.event.tracking.IPhaseState; +import org.spongepowered.common.event.tracking.PhaseContext; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.item.util.ItemStackUtil; +import org.spongepowered.common.util.Preconditions; +import org.spongepowered.common.util.VecHelper; + +public class RecipeContext extends PhaseContext { + + protected PhaseContext priorContext; + ItemStackSnapshot usedItem; + Level world; + BlockState blockState; + BlockPos pos; + SpongeBlockSnapshot snapshot; + SingleItemRecipe recipe; + Identifier recipeID; + AbstractFurnaceBlockEntity furnace; + + protected RecipeContext(final IPhaseState state, final PhaseTracker tracker) { + super(state, tracker); + } + + public RecipeContext provideItem(final ItemStack stack) { + this.usedItem = ItemStackUtil.snapshotOf(stack); + return this; + } + + public ItemStackSnapshot usedItem() { + return this.usedItem; + } + + public RecipeContext world(final Level worldIn) { + this.world = worldIn; + return this; + } + + public RecipeContext block(final BlockState blockState) { + this.blockState = blockState; + return this; + } + + public RecipeContext pos(final BlockPos pos) { + this.pos = pos; + return this; + } + + @Override + public RecipeContext buildAndSwitch() { + Preconditions.checkState(this.pos != null, "BlockPos is null"); + Preconditions.checkState(this.blockState != null, "BlockState is null"); + if (this.usedItem == null) { + this.usedItem = ItemStackSnapshot.empty(); // No used item when growing naturally + } + Preconditions.checkState(this.world != null, "World is null"); + this.priorContext = this.createdTracker.getPhaseContext(); + Preconditions.checkState(this.priorContext != null, "Prior context is null"); + final SpongeBlockSnapshot.BuilderImpl builder = SpongeBlockSnapshot.BuilderImpl.pooled() + .world(((ServerLevel) this.world)) + .position(VecHelper.toVector3i(this.pos)) + .blockState(this.blockState) + .flag(BlockChangeFlags.NONE.withPhysics(true).withUpdateNeighbors(true).withNotifyObservers(true)); + this.priorContext.applyOwnerIfAvailable(builder::creator); + this.priorContext.applyNotifierIfAvailable(builder::notifier); + this.snapshot = builder.build(); + return super.buildAndSwitch(); + } + + @Override + protected void reset() { + super.reset(); + this.priorContext = null; + this.usedItem = null; + this.world = null; + this.blockState = null; + this.pos = null; + this.snapshot = null; + this.furnace = null; + this.recipeID = null; + this.recipe = null; + } + + @Override + protected RecipeContext defensiveCopy(PhaseTracker tracker) { + final RecipeContext newCopy = super.defensiveCopy(tracker); + return newCopy; + } + + public SingleItemRecipe recipe() { + return this.recipe; + } + + public RecipeContext recipe(SingleItemRecipe value) { + this.recipe = value; + return this; + } + + public RecipeContext furnace(AbstractFurnaceBlockEntity entityIn) { + this.world = entityIn.getLevel(); + this.pos = entityIn.getBlockPos(); + this.blockState = entityIn.getBlockState(); + this.furnace = entityIn; + return this; + } + + public RecipeContext recipeID(Identifier identifier) { + this.recipeID = identifier; + return this; + } + + public Identifier recipeID() { + return recipeID; + } + + public AbstractFurnaceBlockEntity furnace() { + return furnace; + } +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/general/RecipeCraftingState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/general/RecipeCraftingState.java new file mode 100644 index 00000000000..77cf85d93f9 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/general/RecipeCraftingState.java @@ -0,0 +1,74 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.tracking.phase.general; + +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.api.world.server.ServerWorld; +import org.spongepowered.common.event.tracking.IPhaseState; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.event.tracking.PooledPhaseState; +import org.spongepowered.common.event.tracking.TrackingUtil; +import org.spongepowered.common.util.VecHelper; + +import java.util.function.BiConsumer; + +@SuppressWarnings({"unchecked", "rawTypes"}) +public class RecipeCraftingState extends PooledPhaseState implements IPhaseState { + + private final BiConsumer FRAME_MODIFIER = super.getFrameModifier() + .andThen((stackFrame, recipeContext) -> { + if (!recipeContext.usedItem.isEmpty()) { + stackFrame.addContext(EventContextKeys.USED_ITEM, recipeContext.usedItem); + } + + if (recipeContext.furnace != null && recipeContext.world instanceof ServerWorld sw) { + stackFrame.addContext(EventContextKeys.LOCATION, ServerLocation.of(sw, VecHelper.toVector3d(recipeContext.pos))); + } + }); + + @Override + public RecipeContext createNewContext(final PhaseTracker tracker) { + return new RecipeContext(this, tracker); + } + + @Override + public void unwind(final RecipeContext phaseContext) { + TrackingUtil.processBlockCaptures(phaseContext); + } + + @Override + public BiConsumer getFrameModifier() { + return this.FRAME_MODIFIER; + } + + private final String desc = TrackingUtil.phaseStateToString("Recipe", this); + + @Override + public String toString() { + return this.desc; + } +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhase.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhase.java index 37803950dc0..079ee2d903a 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhase.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhase.java @@ -31,6 +31,7 @@ import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; import net.minecraft.network.protocol.common.ServerboundKeepAlivePacket; import net.minecraft.network.protocol.common.ServerboundResourcePackPacket; +import net.minecraft.network.protocol.game.ServerboundAttackPacket; import net.minecraft.network.protocol.game.ServerboundChatPacket; import net.minecraft.network.protocol.game.ServerboundClientCommandPacket; import net.minecraft.network.protocol.game.ServerboundCommandSuggestionPacket; @@ -39,6 +40,8 @@ import net.minecraft.network.protocol.game.ServerboundContainerClosePacket; import net.minecraft.network.protocol.game.ServerboundInteractPacket; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; +import net.minecraft.network.protocol.game.ServerboundPickItemFromBlockPacket; +import net.minecraft.network.protocol.game.ServerboundPickItemFromEntityPacket; import net.minecraft.network.protocol.game.ServerboundPlaceRecipePacket; import net.minecraft.network.protocol.game.ServerboundPlayerAbilitiesPacket; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; @@ -53,7 +56,6 @@ import net.minecraft.network.protocol.game.ServerboundUseItemPacket; import net.minecraft.server.level.ServerPlayer; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.accessor.network.protocol.game.ServerboundInteractPacketAccessor; import org.spongepowered.common.event.tracking.IPhaseState; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.phase.packet.drag.DragInventoryAddSlotState; @@ -83,7 +85,6 @@ import org.spongepowered.common.event.tracking.phase.packet.player.AnimationPacketState; import org.spongepowered.common.event.tracking.phase.packet.player.AttackEntityPacketState; import org.spongepowered.common.event.tracking.phase.packet.player.IgnoredPacketState; -import org.spongepowered.common.event.tracking.phase.packet.player.InteractAtEntityPacketState; import org.spongepowered.common.event.tracking.phase.packet.player.InteractEntityPacketState; import org.spongepowered.common.event.tracking.phase.packet.player.InteractionPacketContext; import org.spongepowered.common.event.tracking.phase.packet.player.InteractionPacketState; @@ -113,7 +114,6 @@ public static final class General { static final IPhaseState IGNORED = new IgnoredPacketState(); static final IPhaseState INTERACT_ENTITY = new InteractEntityPacketState(); static final IPhaseState ATTACK_ENTITY = new AttackEntityPacketState(); - static final IPhaseState INTERACT_AT_ENTITY = new InteractAtEntityPacketState(); static final IPhaseState CREATIVE_INVENTORY = new CreativeInventoryPacketState(); static final IPhaseState PLACE_BLOCK = new PlaceBlockPacketState(); static final IPhaseState REQUEST_RESPAWN = new BasicPacketState(); @@ -121,8 +121,6 @@ public static final class General { static final IPhaseState INVALID = new InvalidPacketState(); static final IPhaseState START_RIDING_JUMP = new BasicPacketState(); static final IPhaseState ANIMATION = new AnimationPacketState(); - static final IPhaseState PRESS_SHIFT_KEY = new BasicPacketState(); - static final IPhaseState RELEASE_SHIFT_KEY = new BasicPacketState(); static final IPhaseState START_SPRINTING = new BasicPacketState(); static final IPhaseState STOP_SPRINTING = new BasicPacketState(); static final IPhaseState STOP_SLEEPING = new StopSleepingPacketState(); @@ -234,11 +232,11 @@ public PhaseContext populateContext(final Packet packet, final ServerPlaye // Inventory packet specific methods private static BasicInventoryPacketState fromWindowPacket(final ServerboundContainerClickPacket windowPacket) { - final int mode = 0x01 << 9 << windowPacket.getClickType().ordinal(); - final int packed = windowPacket.getButtonNum(); + final int mode = 0x01 << 9 << windowPacket.containerInput().ordinal(); + final int packed = windowPacket.buttonNum(); final int unpacked = mode == Constants.Networking.MODE_DRAG ? (0x01 << 6 << (packed >> 2 & 3)) | (0x01 << 3 << (packed & 3)) : (0x01 << (packed & 3)); - final BasicInventoryPacketState inventory = PacketPhase.fromState(PacketPhase.clickType(windowPacket.getSlotNum()) | mode | unpacked); + final BasicInventoryPacketState inventory = PacketPhase.fromState(PacketPhase.clickType(windowPacket.slotNum()) | mode | unpacked); if (inventory == PacketPhase.Inventory.INVENTORY) { SpongeCommon.logger().warn(String.format("Unable to find InventoryPacketState handler for click window packet: %s", windowPacket)); } @@ -281,19 +279,8 @@ private static final class Holder { private void setupPacketToStateMapping() { this.packetTranslationMap.put(ServerboundKeepAlivePacket.class, packet -> PacketPhase.General.IGNORED); this.packetTranslationMap.put(ServerboundChatPacket.class, packet -> PacketPhase.General.CHAT_COMMAND); - this.packetTranslationMap.put(ServerboundInteractPacket.class, packet -> { - final ServerboundInteractPacket useEntityPacket = (ServerboundInteractPacket) packet; - final ServerboundInteractPacket.ActionType action = ((ServerboundInteractPacketAccessor) useEntityPacket).accessor$action().getType(); - if (action == ServerboundInteractPacket.ActionType.INTERACT) { - return PacketPhase.General.INTERACT_ENTITY; - } else if (action == ServerboundInteractPacket.ActionType.ATTACK) { - return PacketPhase.General.ATTACK_ENTITY; - } else if (action == ServerboundInteractPacket.ActionType.INTERACT_AT) { - return PacketPhase.General.INTERACT_AT_ENTITY; - } else { - return PacketPhase.General.INVALID; - } - }); + this.packetTranslationMap.put(ServerboundInteractPacket.class, packet -> PacketPhase.General.INTERACT_ENTITY); + this.packetTranslationMap.put(ServerboundAttackPacket.class, packet -> PacketPhase.General.ATTACK_ENTITY); this.packetTranslationMap.put(ServerboundMovePlayerPacket.class, packet -> PacketPhase.General.MOVEMENT); this.packetTranslationMap.put(ServerboundMovePlayerPacket.Pos.class, packet -> PacketPhase.General.MOVEMENT); this.packetTranslationMap.put(ServerboundMovePlayerPacket.Rot.class, packet -> PacketPhase.General.MOVEMENT); @@ -316,6 +303,8 @@ private void setupPacketToStateMapping() { this.packetTranslationMap.put(ServerboundPlayerInputPacket.class, packet -> PacketPhase.General.HANDLED_EXTERNALLY); this.packetTranslationMap.put(ServerboundContainerClosePacket.class, packet -> PacketPhase.General.CLOSE_WINDOW); this.packetTranslationMap.put(ServerboundContainerClickPacket.class, packet -> PacketPhase.fromWindowPacket((ServerboundContainerClickPacket) packet)); + this.packetTranslationMap.put(ServerboundPickItemFromBlockPacket.class, packet -> Inventory.MIDDLE_INVENTORY_CLICK); + this.packetTranslationMap.put(ServerboundPickItemFromEntityPacket.class, packet -> Inventory.MIDDLE_INVENTORY_CLICK); this.packetTranslationMap.put(ServerboundSetCreativeModeSlotPacket.class, packet -> PacketPhase.General.CREATIVE_INVENTORY); this.packetTranslationMap.put(ServerboundContainerButtonClickPacket.class, packet -> PacketPhase.Inventory.ENCHANT_ITEM); this.packetTranslationMap.put(ServerboundSignUpdatePacket.class, packet -> PacketPhase.General.UPDATE_SIGN); @@ -336,8 +325,6 @@ private void setupPacketToStateMapping() { } private static final ImmutableMap>> PLAYER_ACTION_MAPPINGS = ImmutableMap.>>builder() - .put(ServerboundPlayerCommandPacket.Action.PRESS_SHIFT_KEY, PacketPhase.General.PRESS_SHIFT_KEY) - .put(ServerboundPlayerCommandPacket.Action.RELEASE_SHIFT_KEY, PacketPhase.General.RELEASE_SHIFT_KEY) .put(ServerboundPlayerCommandPacket.Action.STOP_SLEEPING, PacketPhase.General.STOP_SLEEPING) .put(ServerboundPlayerCommandPacket.Action.START_SPRINTING, PacketPhase.General.START_SPRINTING) .put(ServerboundPlayerCommandPacket.Action.STOP_SPRINTING, PacketPhase.General.STOP_SPRINTING) diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhaseUtil.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhaseUtil.java index 1c2ab6ee994..70cc4952179 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhaseUtil.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhaseUtil.java @@ -30,16 +30,18 @@ import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.common.ServerboundClientInformationPacket; import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundSetCursorItemPacket; +import net.minecraft.network.protocol.game.ClientboundSetPlayerInventoryPacket; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.animal.Pig; -import net.minecraft.world.entity.animal.Sheep; -import net.minecraft.world.entity.animal.Wolf; -import net.minecraft.world.entity.animal.horse.AbstractChestedHorse; +import net.minecraft.world.entity.animal.equine.AbstractChestedHorse; +import net.minecraft.world.entity.animal.sheep.Sheep; +import net.minecraft.world.entity.animal.wolf.Wolf; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -59,12 +61,12 @@ import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.item.inventory.ItemStackSnapshot; import org.spongepowered.api.item.inventory.transaction.SlotTransaction; -import org.spongepowered.common.accessor.entity.passive.AbstractChestedHorseEntityAccessor; +import org.spongepowered.common.accessor.entity.animal.equine.AbstractChestedHorseEntityAccessor; import org.spongepowered.common.accessor.network.protocol.game.ServerboundMovePlayerPacketAccessor; import org.spongepowered.common.accessor.world.entity.EntityAccessor; -import org.spongepowered.common.accessor.world.entity.animal.PigAccessor; -import org.spongepowered.common.accessor.world.entity.animal.SheepAccessor; -import org.spongepowered.common.accessor.world.entity.animal.WolfAccessor; +import org.spongepowered.common.accessor.world.entity.animal.sheep.SheepAccessor; +import org.spongepowered.common.accessor.world.entity.animal.wolf.WolfAccessor; +import org.spongepowered.common.accessor.world.inventory.AbstractContainerMenuAccessor; import org.spongepowered.common.accessor.world.inventory.SlotAccessor; import org.spongepowered.common.bridge.network.protocol.PacketBridge; import org.spongepowered.common.bridge.world.level.block.TrackableBlockBridge; @@ -82,7 +84,10 @@ public final class PacketPhaseUtil { @SuppressWarnings("removal") public static void handleSlotRestore(@Nullable final Player player, final @Nullable AbstractContainerMenu containerMenu, final List slotTransactions, final boolean eventCancelled) { - try (PhaseContext<@NonNull ?> ignored = BlockPhase.State.RESTORING_BLOCKS.createPhaseContext(PhaseTracker.SERVER).buildAndSwitch()) { + final PhaseTracker phaseTracker = player != null + ? PhaseTracker.getWorldInstance((ServerLevel) player.level()) + : PhaseTracker.getWorldInstance(); + try (PhaseContext<@NonNull ?> ignored = BlockPhase.State.RESTORING_BLOCKS.createPhaseContext(phaseTracker).buildAndSwitch()) { boolean restoredAny = false; for (final SlotTransaction slotTransaction : slotTransactions) { @@ -95,11 +100,11 @@ public static void handleSlotRestore(@Nullable final Player player, final @Nulla if (containerMenu == null || slot.viewedSlot() instanceof Slot) { slot.set(snapshot); } else if (player instanceof ServerPlayer serverPlayer - && containerMenu != player.inventoryMenu && serverPlayer.inventory().containsInventory(slot)) { + && containerMenu != player.inventoryMenu && serverPlayer.inventory().containsInventory(slot)) { final org.spongepowered.api.item.inventory.ItemStack stack = snapshot.asMutable(); slot.set(stack); ((net.minecraft.server.level.ServerPlayer) player).connection.send( - new ClientboundContainerSetSlotPacket(-2, player.inventoryMenu.getStateId(), ((SlotAdapter) slot).getOrdinal(), ItemStackUtil.toNative(stack))); + new ClientboundSetPlayerInventoryPacket(((SlotAdapter) slot).getOrdinal(), ItemStackUtil.toNative(stack))); } else { final int slotNumber = ((SlotAdapter) slot).getOrdinal(); final Slot nmsSlot = containerMenu.getSlot(slotNumber); @@ -132,9 +137,9 @@ public static void handleCursorRestore(final Player player, final Transaction slotTra } @SuppressWarnings({"rawtypes", "unchecked", "deprecation"}) - public static void onProcessPacket(final Packet packetIn, final PacketListener netHandler) { - if (netHandler instanceof ServerGamePacketListenerImpl) { - net.minecraft.server.level.ServerPlayer packetPlayer = ((ServerGamePacketListenerImpl) netHandler).player; + public static void onProcessPacket(final Packet packetIn, final PacketListener netHandler,final Runnable call) { + if (netHandler instanceof ServerGamePacketListenerImpl serverGameImpl) { + net.minecraft.server.level.ServerPlayer packetPlayer = serverGameImpl.player; // Only process the CustomPayload, Respawn & ChunkBatchReceived packets from players if they are dead. if (!packetPlayer.isAlive() && !((PacketBridge) packetIn).bridge$canProcessWhenDead()) { return; @@ -212,8 +218,7 @@ public static void onProcessPacket(final Packet packetIn, final PacketListener n // Don't process movement capture logic if player hasn't moved boolean ignoreMovementCapture; - if (packetIn instanceof ServerboundMovePlayerPacket) { - final ServerboundMovePlayerPacket movingPacket = ((ServerboundMovePlayerPacket) packetIn); + if (packetIn instanceof ServerboundMovePlayerPacket movingPacket) { if (movingPacket instanceof ServerboundMovePlayerPacket.Rot) { ignoreMovementCapture = true; } else if (packetPlayer.getX() == ((ServerboundMovePlayerPacketAccessor) movingPacket).accessor$x() && packetPlayer.getY() == ((ServerboundMovePlayerPacketAccessor) movingPacket).accessor$y() && packetPlayer.getZ() == ((ServerboundMovePlayerPacketAccessor) movingPacket).accessor$z()) { @@ -231,9 +236,9 @@ public static void onProcessPacket(final Packet packetIn, final PacketListener n final BlockPos max = BlockPos.containing(boundingBox.maxX - 0.001D, boundingBox.maxY - 0.001D, boundingBox.maxZ - 0.001D); final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); if (packetPlayer.level().hasChunksAt(min, max)) { - for(int x = min.getX(); x <= max.getX(); ++x) { - for(int y = min.getY(); y <= max.getY(); ++y) { - for(int z = min.getZ(); z <= max.getZ(); ++z) { + for (int x = min.getX(); x <= max.getX(); ++x) { + for (int y = min.getY(); y <= max.getY(); ++y) { + for (int z = min.getZ(); z <= max.getZ(); ++z) { pos.set(x, y, z); final Block block = packetPlayer.level().getBlockState(pos).getBlock(); if (((TrackableBlockBridge) block).bridge$hasEntityInsideLogic()) { @@ -252,10 +257,10 @@ public static void onProcessPacket(final Packet packetIn, final PacketListener n } else { final IPhaseState> packetState = PacketPhase.getInstance().getStateForPacket(packetIn); // At the very least make an unknown packet state case. - final PacketContext context = packetState.createPhaseContext(PhaseTracker.SERVER); + final PacketContext context = packetState.createPhaseContext(PhaseTracker.getWorldInstance(packetPlayer.level())); context.source(packetPlayer) - .packetPlayer(packetPlayer) - .packet(packetIn); + .packetPlayer(packetPlayer) + .packet(packetIn); if (!PacketPhase.getInstance().isPacketInvalid(packetIn, packetPlayer, packetState)) { PacketPhase.getInstance().populateContext(packetIn, packetPlayer, packetState, context); @@ -264,12 +269,13 @@ public static void onProcessPacket(final Packet packetIn, final PacketListener n } try (final PhaseContext packetContext = context) { packetContext.buildAndSwitch(); - packetIn.handle(netHandler); + + call.run(); } } } } else { // client - packetIn.handle(netHandler); + call.run(); } } @@ -277,7 +283,7 @@ public static void onProcessPacket(final Packet packetIn, final PacketListener n * Attempts to find the {@link EntityDataAccessor} that was potentially modified * when a player interacts with an entity. * - * @param stack The item the player is holding + * @param stack The item the player is holding * @param entity The entity * @return A possible data parameter or null if unknown */ @@ -303,10 +309,6 @@ public static void onProcessPacket(final Packet packetIn, final PacketListener n return entity instanceof LivingEntity && !(entity instanceof Player) && stack.has(DataComponents.CUSTOM_NAME) ? EntityAccessor.accessor$DATA_CUSTOM_NAME() : null; } - if (item == Items.SADDLE) { - // ItemSaddle.itemInteractionForEntity - return entity instanceof Pig ? PigAccessor.accessor$DATA_SADDLE_ID() : null; - } if (item instanceof BlockItem && ((BlockItem) item).getBlock() == Blocks.CHEST) { // AbstractChestHorse.processInteract diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketState.java index e6c2cf7122f..c361f3e2fa1 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketState.java @@ -26,7 +26,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.Packet; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.block.Block; @@ -93,7 +93,7 @@ public Supplier getSpawnTypeForTransaction( @Override public Supplier attemptWorldKey(final P context) { - final ResourceLocation worldKey = context.packetPlayer.level().dimension().location(); + final Identifier worldKey = context.packetPlayer.level().dimension().identifier(); return () -> (ResourceKey) (Object) worldKey; } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/AttackEntityPacketState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/AttackEntityPacketState.java index ff50d7dc6b1..a5a36cb0b2e 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/AttackEntityPacketState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/AttackEntityPacketState.java @@ -25,8 +25,7 @@ package org.spongepowered.common.event.tracking.phase.packet.player; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ServerboundInteractPacket; -import net.minecraft.server.level.ServerLevel; +import net.minecraft.network.protocol.game.ServerboundAttackPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.ExperienceOrb; import net.minecraft.world.entity.LivingEntity; @@ -52,9 +51,9 @@ public final class AttackEntityPacketState extends BasicPacketState { private final BiConsumer ATTACK_MODIFIER = super.getFrameModifier().andThen((frame, ctx) -> { frame.addContext(EventContextKeys.USED_ITEM, ctx.getItemUsedSnapshot()); - final ServerboundInteractPacket useEntityPacket = ctx.getPacket(); + final ServerboundAttackPacket useEntityPacket = ctx.getPacket(); final ServerPlayer player = ctx.getPacketPlayer(); - final net.minecraft.world.entity.@Nullable Entity entity = useEntityPacket.getTarget(player.serverLevel()); + final net.minecraft.world.entity.@Nullable Entity entity = player.level().getEntityOrPart(useEntityPacket.entityId()); if (entity != null) { // hand is not populated if we don't have an entity in scope that caused this. frame.addContext(EventContextKeys.USED_HAND, ctx.getHandUsed()); @@ -70,10 +69,10 @@ public BiConsumer getFrameModi @Override public boolean isPacketIgnored(final Packet packetIn, final ServerPlayer packetPlayer) { - final ServerboundInteractPacket useEntityPacket = (ServerboundInteractPacket) packetIn; + final ServerboundAttackPacket useEntityPacket = (ServerboundAttackPacket) packetIn; // There are cases where a player is interacting with an entity that // doesn't exist on the server. - final net.minecraft.world.entity.@Nullable Entity entity = useEntityPacket.getTarget((ServerLevel) packetPlayer.level()); + final net.minecraft.world.entity.@Nullable Entity entity = packetPlayer.level().getEntityOrPart(useEntityPacket.entityId()); return entity == null; } @@ -87,9 +86,9 @@ public void populateContext(final ServerPlayer playerMP, final Packet packet, public Supplier getSpawnTypeForTransaction( final BasicPacketContext context, final net.minecraft.world.entity.Entity entityToSpawn ) { - final ServerboundInteractPacket useEntityPacket = context.getPacket(); + final ServerboundAttackPacket useEntityPacket = context.getPacket(); final ServerPlayer player = context.getPacketPlayer(); - final net.minecraft.world.entity.@Nullable Entity entity = useEntityPacket.getTarget(player.serverLevel()); + final net.minecraft.world.entity.@Nullable Entity entity = player.level().getEntityOrPart(useEntityPacket.entityId()); if (entity != null && (entity.isRemoved() || entity instanceof LivingEntity && ((LivingEntity) entity).isDeadOrDying())) { return entityToSpawn instanceof ExperienceOrb ? SpawnTypes.EXPERIENCE : SpawnTypes.DROPPED_ITEM; } @@ -102,9 +101,9 @@ public Supplier getSpawnTypeForTransaction( @Override public void unwind(final BasicPacketContext context) { if (!TrackingUtil.processBlockCaptures(context)) { - final ServerboundInteractPacket useEntityPacket = context.getPacket(); + final ServerboundAttackPacket useEntityPacket = context.getPacket(); final ServerPlayer player = context.getPacketPlayer(); - final net.minecraft.world.entity.@Nullable Entity entity = useEntityPacket.getTarget(player.serverLevel()); + final net.minecraft.world.entity.@Nullable Entity entity = player.level().getEntityOrPart(useEntityPacket.entityId()); if (entity instanceof Entity) { ((Entity) entity).offer(Keys.NOTIFIER, player.getUUID()); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/InteractAtEntityPacketState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/InteractAtEntityPacketState.java deleted file mode 100644 index 02a3a781ff5..00000000000 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/InteractAtEntityPacketState.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.event.tracking.phase.packet.player; - -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ServerboundInteractPacket; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.data.Keys; -import org.spongepowered.api.data.type.HandType; -import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.world.World; -import org.spongepowered.common.accessor.network.protocol.game.ServerboundInteractPacketAccessor; -import org.spongepowered.common.accessor.network.protocol.game.ServerboundInteractPacket_InteractionAtLocationActionAccessor; -import org.spongepowered.common.bridge.CreatorTrackedBridge; -import org.spongepowered.common.entity.PlayerTracker; -import org.spongepowered.common.event.tracking.TrackingUtil; -import org.spongepowered.common.event.tracking.phase.packet.BasicPacketContext; -import org.spongepowered.common.event.tracking.phase.packet.BasicPacketState; -import org.spongepowered.common.item.util.ItemStackUtil; - -public final class InteractAtEntityPacketState extends BasicPacketState { - - @Override - public boolean isPacketIgnored(final Packet packetIn, final ServerPlayer packetPlayer) { - final ServerboundInteractPacket useEntityPacket = (ServerboundInteractPacket) packetIn; - // There are cases where a player is interacting with an entity that doesn't exist on the server. - final net.minecraft.world.entity.@Nullable Entity entity = useEntityPacket.getTarget((ServerLevel) packetPlayer.level()); - return entity == null; - } - - @Override - public void populateContext(final ServerPlayer playerMP, final Packet packet, final BasicPacketContext context) { - final ServerboundInteractPacket useEntityPacket = (ServerboundInteractPacket) packet; - final ServerboundInteractPacket_InteractionAtLocationActionAccessor accessor = (ServerboundInteractPacket_InteractionAtLocationActionAccessor) ((ServerboundInteractPacketAccessor) useEntityPacket).accessor$action(); - final ItemStack stack = ItemStackUtil.cloneDefensive(playerMP.getItemInHand(accessor.accessor$hand())); - if (stack != null) { - context.itemUsed(stack); - } - final HandType handType = (HandType) (Object) accessor.accessor$hand(); - context.handUsed(handType); - } - - @Override - public void unwind(final BasicPacketContext context) { - final ServerPlayer player = context.getPacketPlayer(); - - final ServerboundInteractPacket useEntityPacket = context.getPacket(); - final net.minecraft.world.entity.Entity entity = useEntityPacket.getTarget((ServerLevel) player.level()); - if (entity == null) { - // Something happened? - return; - } - final World spongeWorld = (World) player.level(); - if (entity instanceof CreatorTrackedBridge) { - ((CreatorTrackedBridge) entity).tracker$setTrackedUUID(PlayerTracker.Type.CREATOR, player.getUUID()); - } else { - ((Entity) entity).offer(Keys.NOTIFIER, player.getUUID()); - } - - - // TODO - Determine if we need to pass the supplier or perform some parameterized - // process if not empty method on the capture object. - TrackingUtil.processBlockCaptures(context); - } - - -} diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/InteractEntityPacketState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/InteractEntityPacketState.java index a4c42910665..976ed38c017 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/InteractEntityPacketState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/InteractEntityPacketState.java @@ -32,8 +32,6 @@ import org.spongepowered.api.data.type.HandType; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.common.accessor.network.protocol.game.ServerboundInteractPacketAccessor; -import org.spongepowered.common.accessor.network.protocol.game.ServerboundInteractPacket_InteractionActionAccessor; import org.spongepowered.common.bridge.CreatorTrackedBridge; import org.spongepowered.common.entity.PlayerTracker; import org.spongepowered.common.event.tracking.TrackingUtil; @@ -43,36 +41,37 @@ public final class InteractEntityPacketState extends BasicPacketState { + @SuppressWarnings("deprecation") // getEntityOrPart is the easiest way to check if the entity is valid @Override public boolean isPacketIgnored(final Packet packetIn, final ServerPlayer packetPlayer) { final ServerboundInteractPacket useEntityPacket = (ServerboundInteractPacket) packetIn; // There are cases where a player is interacting with an entity that doesn't exist on the server. - final net.minecraft.world.entity.@Nullable Entity entity = useEntityPacket.getTarget(packetPlayer.serverLevel()); + final net.minecraft.world.entity.@Nullable Entity entity = packetPlayer.level().getEntityOrPart(useEntityPacket.entityId()); return entity == null; } + @SuppressWarnings("deprecation") // getEntityOrPart is the easiest way to check if the entity is valid @Override public void populateContext(final ServerPlayer player, final Packet packet, final BasicPacketContext context) { final ServerboundInteractPacket useEntityPacket = (ServerboundInteractPacket) packet; - final net.minecraft.world.entity.Entity entity = useEntityPacket.getTarget(player.serverLevel()); + final net.minecraft.world.entity.@Nullable Entity entity = player.level().getEntityOrPart(useEntityPacket.entityId()); if (entity != null) { - final ServerboundInteractPacket_InteractionActionAccessor accessor = (ServerboundInteractPacket_InteractionActionAccessor) ((ServerboundInteractPacketAccessor) useEntityPacket).accessor$action(); - final ItemStack stack = ItemStackUtil.cloneDefensive(player.getItemInHand(accessor.accessor$hand())); + final ItemStack stack = ItemStackUtil.cloneDefensive(player.getItemInHand(useEntityPacket.hand())); if (stack != null) { context.itemUsed(stack); } - final HandType handType = (HandType) (Object) accessor.accessor$hand(); + final HandType handType = (HandType) (Object) useEntityPacket.hand(); context.handUsed(handType); } } + @SuppressWarnings("deprecation") // getEntityOrPart is the easiest way to check if the entity is valid @Override public void unwind(final BasicPacketContext phaseContext) { - final ServerPlayer player = phaseContext.getPacketPlayer(); final ServerboundInteractPacket useEntityPacket = phaseContext.getPacket(); - final net.minecraft.world.entity.Entity entity = useEntityPacket.getTarget(player.serverLevel()); + final net.minecraft.world.entity.@Nullable Entity entity = player.level().getEntityOrPart(useEntityPacket.entityId()); if (entity == null) { // Something happened? return; diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/PacketCommandState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/PacketCommandState.java index 476bf9ec2a8..dca829b7ddc 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/PacketCommandState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/PacketCommandState.java @@ -35,6 +35,7 @@ import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.cause.entity.SpawnTypes; import org.spongepowered.api.event.entity.SpawnEntityEvent; +import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.event.tracking.TrackingUtil; import org.spongepowered.common.event.tracking.context.transaction.GameTransaction; @@ -66,7 +67,7 @@ public BiConsumer getFr @Override public void postBlockTransactionApplication( - final PlayerCommandPhaseContext context, final BlockChange blockChange, + final PlayerCommandPhaseContext context, ServerWorld serverWorld, final BlockChange blockChange, final BlockTransactionReceipt transaction ) { // We want to investigate if there is a user on the cause stack diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/PlaceBlockPacketState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/PlaceBlockPacketState.java index 1d5035d2cff..980c0bba53a 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/PlaceBlockPacketState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/PlaceBlockPacketState.java @@ -87,7 +87,7 @@ public void populateContext(final net.minecraft.server.level.ServerPlayer player @Override public void postBlockTransactionApplication( - final BasicPacketContext context, final BlockChange blockChange, + final BasicPacketContext context, ServerWorld serverWorld, final BlockChange blockChange, final BlockTransactionReceipt transaction ) { TrackingUtil.associateTrackerToTarget(blockChange, transaction, ((ServerPlayer) context.getPacketPlayer()).uniqueId()); diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/UseItemPacketState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/UseItemPacketState.java index 3da4933b65b..dff35848e9c 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/UseItemPacketState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/UseItemPacketState.java @@ -41,6 +41,7 @@ import org.spongepowered.api.event.cause.entity.SpawnTypes; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.item.inventory.ItemStackSnapshot; +import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge; import org.spongepowered.common.entity.PlayerTracker; import org.spongepowered.common.event.tracking.PhaseTracker; @@ -91,7 +92,7 @@ public void populateContext( @Override public void postBlockTransactionApplication( - final BasicPacketContext context, final BlockChange blockChange, + final BasicPacketContext context, ServerWorld serverWorld, final BlockChange blockChange, final BlockTransactionReceipt transaction ) { final ServerPlayer player = context.getSpongePlayer(); diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/player/PlayerInteractPhase.java b/src/main/java/org/spongepowered/common/event/tracking/phase/player/PlayerInteractPhase.java index 1e72da24198..6739b60384a 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/player/PlayerInteractPhase.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/player/PlayerInteractPhase.java @@ -35,6 +35,7 @@ import org.spongepowered.api.event.cause.entity.SpawnType; import org.spongepowered.api.event.cause.entity.SpawnTypes; import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.common.bridge.world.TrackedWorldBridge; import org.spongepowered.common.bridge.world.level.TrackableBlockEventDataBridge; import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge; @@ -62,9 +63,9 @@ public void unwind(final PlayerInteractContext context) { @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public void postBlockTransactionApplication(PlayerInteractContext context, BlockChange blockChange, BlockTransactionReceipt receipt) { + public void postBlockTransactionApplication(PlayerInteractContext context, ServerWorld world, BlockChange blockChange, BlockTransactionReceipt receipt) { if (context.forward() != null && context.forwardContext() != null) { - ((IPhaseState) context.forward()).postBlockTransactionApplication(context.forwardContext(), blockChange, receipt); + ((IPhaseState) context.forward()).postBlockTransactionApplication(context.forwardContext(), world, blockChange, receipt); return; } // When there is no forwarding state, apply tracker association directly from the context diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/BlockEventTickPhaseState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/BlockEventTickPhaseState.java index ab67914b419..c0bd38061b6 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/BlockEventTickPhaseState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/BlockEventTickPhaseState.java @@ -34,6 +34,7 @@ import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.cause.entity.SpawnType; import org.spongepowered.api.event.cause.entity.SpawnTypes; +import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.common.block.SpongeBlockSnapshot; import org.spongepowered.common.bridge.world.level.TrackableBlockEventDataBridge; import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge; @@ -84,7 +85,7 @@ public void associateNeighborStateNotifier( @Override public void postBlockTransactionApplication( - final BlockEventTickContext context, final BlockChange blockChange, + final BlockEventTickContext context, ServerWorld serverWorld, final BlockChange blockChange, final BlockTransactionReceipt receipt ) { final Block block = (Block) receipt.originalBlock().state().type(); diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/BlockTickPhaseState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/BlockTickPhaseState.java index 26c145f1376..8eee3c662b6 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/BlockTickPhaseState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/BlockTickPhaseState.java @@ -26,7 +26,7 @@ import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; -import net.minecraft.world.level.Level; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.BonemealableBlock; @@ -73,9 +73,9 @@ public BlockChange associateBlockChangeWithSnapshot( if (newBlock instanceof BonemealableBlock) { return BlockChange.GROW; } else if (phaseContext.tickingBlock instanceof final StemBlock stemBlock) { - final Registry registry = ((Level) phaseContext.world).registryAccess().registryOrThrow(Registries.BLOCK); - final @Nullable Block fruitBlock = registry.get(((StemBlockAccessor) stemBlock).accessor$fruit()); - final @Nullable Block attachedStemBlock = registry.get(((StemBlockAccessor) stemBlock).accessor$attachedStem()); + final Registry registry = ((ServerLevel) phaseContext.world).registryAccess().lookupOrThrow(Registries.BLOCK); + final @Nullable Block fruitBlock = registry.getValue(((StemBlockAccessor) stemBlock).accessor$fruit()); + final @Nullable Block attachedStemBlock = registry.getValue(((StemBlockAccessor) stemBlock).accessor$attachedStem()); if (newBlock == fruitBlock || newBlock == attachedStemBlock) { return BlockChange.GROW; } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/EntityTickPhaseState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/EntityTickPhaseState.java index 27405e72373..6dfae53e538 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/EntityTickPhaseState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/EntityTickPhaseState.java @@ -40,6 +40,7 @@ import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.cause.entity.SpawnType; import org.spongepowered.api.event.cause.entity.SpawnTypes; +import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.common.accessor.world.entity.decoration.ItemFrameAccessor; import org.spongepowered.common.bridge.world.entity.EntityBridge; import org.spongepowered.common.bridge.world.entity.TrackableEntityBridge; @@ -109,12 +110,12 @@ public Supplier attemptWorldKey( final net.minecraft.world.entity.Entity entity = context.getSource(net.minecraft.world.entity.Entity.class) .orElseThrow( () -> new IllegalStateException("Expected to be ticking an entity, but we're not ticking an entity")); - if (entity.level().isClientSide) { + if (entity.level().isClientSide()) { return () -> { throw new IllegalStateException("attempting a world key on the client???"); }; } - return () -> (ResourceKey) (Object) entity.level().dimension().location(); + return () -> (ResourceKey) (Object) entity.level().dimension().identifier(); } @Override @@ -124,7 +125,7 @@ protected EntityTickContext createNewContext(final PhaseTracker tracker) { @Override public void postBlockTransactionApplication( - final EntityTickContext context, final BlockChange blockChange, + final EntityTickContext context, final ServerWorld serverWorld, final BlockChange blockChange, final BlockTransactionReceipt transaction ) { if (blockChange == BlockChange.BREAK) { @@ -159,12 +160,11 @@ public void postBlockTransactionApplication( return false; }); for (final HangingEntity entityHanging : hangingEntities) { - if (entityHanging instanceof ItemFrame) { - final ItemFrame itemFrame = (ItemFrame) entityHanging; - if (!itemFrame.isRemoved()) { - ((ItemFrameAccessor) itemFrame).invoker$dropItem((net.minecraft.world.entity.Entity) tickingEntity, true); + if (entityHanging instanceof ItemFrame iframe) { + if (!iframe.isRemoved()) { + ((ItemFrameAccessor) iframe).invoker$dropItem((ServerLevel) serverWorld, (net.minecraft.world.entity.Entity) tickingEntity, true); } - itemFrame.remove(net.minecraft.world.entity.Entity.RemovalReason.DISCARDED); + iframe.remove(net.minecraft.world.entity.Entity.RemovalReason.DISCARDED); } } } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/FluidTickContext.java b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/FluidTickContext.java index 1dfe23f74a7..1598a787719 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/FluidTickContext.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/FluidTickContext.java @@ -25,7 +25,6 @@ package org.spongepowered.common.event.tracking.phase.tick; import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; import org.spongepowered.api.world.LocatableBlock; @@ -50,7 +49,7 @@ public FluidTickContext source(final Object owner) { if (owner instanceof LocatableBlock) { final LocatableBlock locatableBlock = (LocatableBlock) owner; final Block block = ((BlockState) locatableBlock.blockState()).getBlock(); - this.providesModifier = !(block instanceof LiquidBlock); + this.providesModifier = true; this.world = locatableBlock.world(); if (block instanceof TrackableBridge) { final TrackableBridge trackable = (TrackableBridge) block; diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/LocationBasedTickPhaseState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/LocationBasedTickPhaseState.java index edb6df776fb..ecce9ee5019 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/LocationBasedTickPhaseState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/LocationBasedTickPhaseState.java @@ -35,6 +35,7 @@ import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.world.LocatableBlock; import org.spongepowered.api.world.World; +import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.common.block.SpongeBlockSnapshot; import org.spongepowered.common.bridge.world.TrackedWorldBridge; import org.spongepowered.common.bridge.world.level.TrackableBlockEventDataBridge; @@ -107,7 +108,7 @@ public void appendNotifierToBlockEvent( public Supplier attemptWorldKey(final T context) { return MemoizedSupplier.memoize(() -> { final World<@NonNull ?, @NonNull ?> world = this.getLocatableBlockSourceFromContext(context).world(); - return (ResourceKey) (Object) ((Level) world).dimension().location(); + return (ResourceKey) (Object) ((Level) world).dimension().identifier(); }); } @@ -122,7 +123,7 @@ public void appendContextPreExplosion(final ExplosionContext explosionContext, f @Override public void postBlockTransactionApplication( - final T context, final BlockChange blockChange, + final T context, ServerWorld serverWorld, final BlockChange blockChange, final BlockTransactionReceipt receipt ) { // If we do not have a notifier at this point then there is no need to attempt to retrieve one from the chunk diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/TickPhase.java b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/TickPhase.java index aee13b8e122..080a96839b6 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/TickPhase.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/TickPhase.java @@ -40,7 +40,7 @@ public static final class Tick { public static final IPhaseState TILE_ENTITY = new TileEntityTickPhaseState(); public static final IPhaseState BLOCK_EVENT = new BlockEventTickPhaseState(); public static final IPhaseState PLAYER = new PlayerTickPhaseState(); - public static final IPhaseState WEATHER = new WeatherTickPhaseState(); + public static final IPhaseState WEATHER = new WeatherTickPhaseState(); public static final IPhaseState SERVER_TICK = new ServerTickState(); public static final IPhaseState WORLD_TICK = new WorldTickState(); diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/WorldTickState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/WorldTickState.java index 76068d6e12f..56eea3c6873 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/tick/WorldTickState.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/tick/WorldTickState.java @@ -68,7 +68,7 @@ public void unwind(final WorldTickContext phaseContext) { public Supplier attemptWorldKey( final WorldTickContext context ) { - return () -> (ResourceKey) (Object) Objects.requireNonNull(context.serverWorld.get(), "ServerWorld reference lost during tick").dimension().location(); + return () -> (ResourceKey) (Object) Objects.requireNonNull(context.serverWorld.get(), "ServerWorld reference lost during tick").dimension().identifier(); } public static class WorldTickContext extends TickContext { diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/world/dragon/DragonContext.java b/src/main/java/org/spongepowered/common/event/tracking/phase/world/dragon/DragonContext.java index db84c4f5088..f8e83634e8a 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/world/dragon/DragonContext.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/world/dragon/DragonContext.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.event.tracking.phase.world.dragon; -import net.minecraft.world.level.dimension.end.EndDragonFight; +import net.minecraft.world.level.dimension.end.EnderDragonFight; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.event.tracking.IPhaseState; import org.spongepowered.common.event.tracking.PhaseContext; @@ -35,18 +35,18 @@ @SuppressWarnings("unchecked") public abstract class DragonContext> extends PhaseContext { - private @Nullable EndDragonFight manager; + private @Nullable EnderDragonFight manager; public DragonContext(final IPhaseState state, final PhaseTracker tracker) { super(state, tracker); } - public D manager(EndDragonFight manager) { + public D manager(EnderDragonFight manager) { this.manager = Objects.requireNonNull(manager); return (D) this; } - public EndDragonFight getManager() { + public EnderDragonFight getManager() { return Objects.requireNonNull(this.manager); } } diff --git a/src/main/java/org/spongepowered/common/fluid/SpongeFluidStateBuilder.java b/src/main/java/org/spongepowered/common/fluid/SpongeFluidStateBuilder.java index 446f781bfe3..6a9ee664dd9 100644 --- a/src/main/java/org/spongepowered/common/fluid/SpongeFluidStateBuilder.java +++ b/src/main/java/org/spongepowered/common/fluid/SpongeFluidStateBuilder.java @@ -32,7 +32,7 @@ import net.minecraft.commands.arguments.blocks.BlockStateParser; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.material.Fluid; @@ -126,7 +126,7 @@ private org.spongepowered.api.fluid.FluidState parseString(final String string) private Fluid readFluid(final StringReader reader) throws CommandSyntaxException { final int cursor = reader.getCursor(); - final ResourceLocation fluidKey = ResourceLocation.read(reader); + final Identifier fluidKey = Identifier.read(reader); final Registry fluidRegistry = SpongeCommon.vanillaRegistry(Registries.FLUID); return fluidRegistry.getOptional(fluidKey).orElseThrow(() -> { reader.setCursor(cursor); diff --git a/src/main/java/org/spongepowered/common/hooks/EntityHooks.java b/src/main/java/org/spongepowered/common/hooks/EntityHooks.java index 471609a73b9..8b600f5147f 100644 --- a/src/main/java/org/spongepowered/common/hooks/EntityHooks.java +++ b/src/main/java/org/spongepowered/common/hooks/EntityHooks.java @@ -25,7 +25,7 @@ package org.spongepowered.common.hooks; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.boss.EnderDragonPart; +import net.minecraft.world.entity.boss.enderdragon.EnderDragonPart; public interface EntityHooks { diff --git a/src/main/java/org/spongepowered/common/hooks/EventHooks.java b/src/main/java/org/spongepowered/common/hooks/EventHooks.java index af90ddb14a9..f12129607a7 100644 --- a/src/main/java/org/spongepowered/common/hooks/EventHooks.java +++ b/src/main/java/org/spongepowered/common/hooks/EventHooks.java @@ -24,14 +24,8 @@ */ package org.spongepowered.common.hooks; -import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; -import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.event.tracking.PhaseTracker; /** * Event hooks for when there is no specific event translation between Sponge @@ -39,14 +33,6 @@ */ public interface EventHooks { - default ChangeEntityWorldEvent.Pre callChangeEntityWorldEventPre(final Entity entity, final ServerLevel toWorld) { - final ChangeEntityWorldEvent.Pre event = SpongeEventFactory.createChangeEntityWorldEventPre(PhaseTracker.getInstance().currentCause(), - (org.spongepowered.api.entity.Entity) entity, (org.spongepowered.api.world.server.ServerWorld) entity.getCommandSenderWorld(), - (org.spongepowered.api.world.server.ServerWorld) toWorld, (org.spongepowered.api.world.server.ServerWorld) toWorld); - SpongeCommon.post(event); - return event; - } - default boolean callPlayerDestruction(ServerPlayer serverPlayer, DamageSource cause) { return false; } diff --git a/src/main/java/org/spongepowered/common/hooks/GeneralHooks.java b/src/main/java/org/spongepowered/common/hooks/GeneralHooks.java index f0d91d4c46b..1932195b812 100644 --- a/src/main/java/org/spongepowered/common/hooks/GeneralHooks.java +++ b/src/main/java/org/spongepowered/common/hooks/GeneralHooks.java @@ -27,6 +27,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.status.ServerStatus; import org.spongepowered.api.Sponge; +import org.spongepowered.common.event.tracking.PhaseTracker; import java.util.Optional; @@ -36,7 +37,7 @@ default boolean onServerThread() { // Return true when the server isn't yet initialized, this means on a client // that the game is still being loaded. This is needed to support initialization // events with cause tracking. - return !Sponge.isServerAvailable() || Sponge.server().onMainThread(); + return !Sponge.isServerAvailable() || PhaseTracker.getWorldInstance().onSidedThread(); } default ServerStatus createServerStatus(ServerStatus originalStatus, Component description, Optional players, Optional version, Optional favicon) { diff --git a/src/main/java/org/spongepowered/common/hooks/WorldHooks.java b/src/main/java/org/spongepowered/common/hooks/WorldHooks.java index 78980e4de88..132b4bca4d9 100644 --- a/src/main/java/org/spongepowered/common/hooks/WorldHooks.java +++ b/src/main/java/org/spongepowered/common/hooks/WorldHooks.java @@ -24,9 +24,13 @@ */ package org.spongepowered.common.hooks; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.LevelStem; +import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.event.tracking.PhaseTracker; public interface WorldHooks { @@ -42,4 +46,11 @@ default boolean isRestoringBlocks(final Level world) { default void postLoadWorld(ServerLevel world) { } default void preUnloadWorld(ServerLevel world) { } + + default Registry earlyRegistryAccess(ResourceKey> levelStem) { + // LEVEL_STEM lives in the server's composite registryAccess() but not in Sponge's scoped + // holder (which excludes DIMENSION_REGISTRIES during RegistryDataLoader processing), so we + // must bypass SpongeCommon.vanillaRegistry() and hit server().registryAccess() directly. + return SpongeCommon.server().registryAccess().lookupOrThrow(levelStem); + } } diff --git a/src/main/java/org/spongepowered/common/inject/SpongeModule.java b/src/main/java/org/spongepowered/common/inject/SpongeModule.java index f20dc056a25..2e7d34909ad 100644 --- a/src/main/java/org/spongepowered/common/inject/SpongeModule.java +++ b/src/main/java/org/spongepowered/common/inject/SpongeModule.java @@ -25,6 +25,9 @@ package org.spongepowered.common.inject; import com.google.inject.AbstractModule; +import org.spongepowered.api.Client; +import org.spongepowered.api.Server; +import org.spongepowered.api.Sponge; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.inject.provider.ConfigDirAnnotation; @@ -35,6 +38,8 @@ public final class SpongeModule extends AbstractModule { @Override protected void configure() { this.bind(Path.class).annotatedWith(ConfigDirAnnotation.SHARED).toInstance(SpongeCommon.pluginConfigDirectory()); - } + this.bind(Server.class).toProvider(Sponge::server); + this.bind(Client.class).toProvider(Sponge::client); + } } diff --git a/src/main/java/org/spongepowered/common/inject/plugin/PublicPluginModule.java b/src/main/java/org/spongepowered/common/inject/plugin/PluginDependencyModule.java similarity index 70% rename from src/main/java/org/spongepowered/common/inject/plugin/PublicPluginModule.java rename to src/main/java/org/spongepowered/common/inject/plugin/PluginDependencyModule.java index 49d633ce16a..0074200b18d 100644 --- a/src/main/java/org/spongepowered/common/inject/plugin/PublicPluginModule.java +++ b/src/main/java/org/spongepowered/common/inject/plugin/PluginDependencyModule.java @@ -26,6 +26,7 @@ import com.google.inject.AbstractModule; import org.spongepowered.api.Sponge; +import org.spongepowered.api.plugin.PluginManager; import org.spongepowered.common.launch.plugin.SpongePluginContainer; import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.metadata.model.PluginDependency; @@ -35,36 +36,39 @@ * Contains the values that are publicly visible and shared * across dependencies. */ -public final class PublicPluginModule extends AbstractModule { +public final class PluginDependencyModule extends AbstractModule { private final PluginContainer container; - PublicPluginModule(final PluginContainer container) { + PluginDependencyModule(final PluginContainer container) { this.container = container; } @Override protected void configure() { final BindingHelper bindingHelper = new BindingHelper(this.binder().withSource(BindingHelper.class)); + final PluginManager manager = Sponge.pluginManager(); + for (final PluginDependency dependency : this.container.metadata().dependencies()) { if (dependency.loadOrder() != PluginDependency.LoadOrder.AFTER) { continue; } - Sponge.pluginManager().plugin(dependency.id()) - .flatMap(p -> ((SpongePluginContainer) p).injector()) - .ifPresent(bindingHelper::bindFrom); + if (manager.plugin(dependency.id()).orElse(null) instanceof SpongePluginContainer spongeContainer) { + spongeContainer.injector().ifPresent(bindingHelper::bindFrom); + } } // Indirect dependencies - Sponge.pluginManager() - .plugins() - .stream() - .filter(p -> p.metadata().dependency(this.container.metadata().id()) - .map(PluginDependency::loadOrder) - .orElse(PluginDependency.LoadOrder.UNDEFINED) == PluginDependency.LoadOrder.BEFORE) - .flatMap(p -> ((SpongePluginContainer) p).injector().stream()) - .forEach(bindingHelper::bindFrom); + for (final PluginContainer other : manager.plugins()) { + if (other.metadata().dependency(this.container.metadata().id()).map(PluginDependency::loadOrder).orElse(PluginDependency.LoadOrder.UNDEFINED) != PluginDependency.LoadOrder.BEFORE) { + continue; + } + + if (other instanceof SpongePluginContainer spongeContainer) { + spongeContainer.injector().ifPresent(bindingHelper::bindFrom); + } + } bindingHelper.bind(); } diff --git a/src/main/java/org/spongepowered/common/inject/plugin/PluginEntrypointModule.java b/src/main/java/org/spongepowered/common/inject/plugin/PluginEntrypointModule.java new file mode 100644 index 00000000000..fbd57ae8046 --- /dev/null +++ b/src/main/java/org/spongepowered/common/inject/plugin/PluginEntrypointModule.java @@ -0,0 +1,53 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.inject.plugin; + +import com.google.inject.PrivateModule; +import com.google.inject.Scopes; + +import java.util.Collection; + + +/** + * A private module installed for each plugin. + * Contains the entrypoint classes. + */ +public final class PluginEntrypointModule extends PrivateModule { + + private final Collection> pluginClasses; + + PluginEntrypointModule(final Collection> pluginClasses) { + this.pluginClasses = pluginClasses; + } + + @Override + protected void configure() { + for (final Class pluginClass : this.pluginClasses) { + this.bind(pluginClass).in(Scopes.SINGLETON); + this.expose(pluginClass); + } + } + +} diff --git a/src/main/java/org/spongepowered/common/inject/plugin/PluginGuice.java b/src/main/java/org/spongepowered/common/inject/plugin/PluginGuice.java index ca7fcad4725..9e15381687f 100644 --- a/src/main/java/org/spongepowered/common/inject/plugin/PluginGuice.java +++ b/src/main/java/org/spongepowered/common/inject/plugin/PluginGuice.java @@ -24,24 +24,31 @@ */ package org.spongepowered.common.inject.plugin; -import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.launch.plugin.SpongePluginContainer; -import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; import java.util.List; public final class PluginGuice { - public static Injector create(final PluginContainer container, final Class pluginClass, final @Nullable Injector platformInjector) { - final List modules = new ArrayList<>(List.of(new PublicPluginModule(container), new PrivatePluginModule(container, pluginClass))); + public static Injector createMain(final SpongePluginContainer container, final Collection> pluginClasses) { + final List modules = new LinkedList<>(); + modules.add(new PluginDependencyModule(container)); + modules.add(new PluginEntrypointModule(pluginClasses)); + modules.add(new PrivatePluginModule(container)); + + final Launch launch = Launch.instance(); + launch.modModule(container).ifPresent(modules::add); final @Nullable Object customModule = container.metadata().property("guice-module").orElse(null); if (customModule != null) { try { - final Class moduleClass = Class.forName(customModule.toString(), true, pluginClass.getClassLoader()); + final Class moduleClass = Class.forName(customModule.toString()); final Module moduleInstance = (Module) moduleClass.getConstructor().newInstance(); modules.add(moduleInstance); } catch (final Exception ex) { @@ -49,12 +56,10 @@ public static Injector create(final PluginContainer container, final Class pl } } - final Module module = new PriorityOverrideModule(modules); + return launch.lifecycle().platformInjector().createChildInjector(new PriorityOverrideModule(modules)); + } - if (platformInjector != null) { - return platformInjector.createChildInjector(module); - } else { - return Guice.createInjector(module); - } + public static Injector createChild(final SpongePluginContainer container, final Collection> pluginClasses) { + return container.injector().get().createChildInjector(new PluginEntrypointModule(pluginClasses)); } } diff --git a/src/main/java/org/spongepowered/common/inject/plugin/PrivatePluginModule.java b/src/main/java/org/spongepowered/common/inject/plugin/PrivatePluginModule.java index 4b52f073d9c..c385eeb2583 100644 --- a/src/main/java/org/spongepowered/common/inject/plugin/PrivatePluginModule.java +++ b/src/main/java/org/spongepowered/common/inject/plugin/PrivatePluginModule.java @@ -31,28 +31,21 @@ import org.spongepowered.common.inject.provider.PluginConfigurationModule; import org.spongepowered.plugin.PluginContainer; - /** - * A module installed for each plugin. - * + * A private module installed for each plugin. * Contains the values that are not shared for dependencies or - * that require access to the private resources, like the plugin class. + * that require access to the private resources, like the plugin container. */ public final class PrivatePluginModule extends PrivateModule { private final PluginContainer container; - private final Class pluginClass; - PrivatePluginModule(final PluginContainer container, final Class pluginClass) { + PrivatePluginModule(final PluginContainer container) { this.container = container; - this.pluginClass = pluginClass; } @Override protected void configure() { - this.bind(this.pluginClass).in(Scopes.SINGLETON); - this.expose(this.pluginClass); - this.install(new InjectionPointProvider()); this.bind(PluginContainer.class).toInstance(this.container); diff --git a/src/main/java/org/spongepowered/common/inventory/custom/CarriedWrapperInventory.java b/src/main/java/org/spongepowered/common/inventory/custom/CarriedWrapperInventory.java index d31b8dd6218..2797832abe3 100644 --- a/src/main/java/org/spongepowered/common/inventory/custom/CarriedWrapperInventory.java +++ b/src/main/java/org/spongepowered/common/inventory/custom/CarriedWrapperInventory.java @@ -104,16 +104,6 @@ public boolean stillValid(Player player) { return this.wrapped.stillValid(player); } - @Override - public void startOpen(Player player) { - this.wrapped.startOpen(player); - } - - @Override - public void stopOpen(Player player) { - this.wrapped.stopOpen(player); - } - @Override public boolean canPlaceItem(int index, ItemStack stack) { return this.wrapped.canPlaceItem(index, stack); @@ -133,4 +123,5 @@ public boolean hasAnyOf(Set set) { public void clearContent() { this.wrapped.clearContent(); } + } diff --git a/src/main/java/org/spongepowered/common/inventory/custom/SpongeInventoryMenu.java b/src/main/java/org/spongepowered/common/inventory/custom/SpongeInventoryMenu.java index de70c7cf663..5d27c05c268 100644 --- a/src/main/java/org/spongepowered/common/inventory/custom/SpongeInventoryMenu.java +++ b/src/main/java/org/spongepowered/common/inventory/custom/SpongeInventoryMenu.java @@ -29,7 +29,7 @@ import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.inventory.ContainerListener; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; @@ -125,7 +125,7 @@ public void setTitle(final Component title) { this.title = title; this.tracked.forEach((c, p) -> { var container = (AbstractContainerMenu) c; - var connection = (ServerGamePacketListenerImpl) p.connection(); + var connection = (ServerGamePacketListenerImpl) p.connectionState(); var packet = new ClientboundOpenScreenPacket(container.containerId, container.getType(), SpongeAdventure.asVanilla(this.title)); connection.send(packet); container.broadcastFullState(); @@ -232,11 +232,11 @@ public void onClose(final Player player, final Container container) { } public boolean onClick( - final int slotId, final int dragType, final ClickType clickTypeIn, final Player player, final Container container) { + final int slotId, final int dragType, final ContainerInput clickTypeIn, final Player player, final Container container) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(player); final Cause cause = frame.currentCause(); - if (clickTypeIn == ClickType.QUICK_CRAFT) { + if (clickTypeIn == ContainerInput.QUICK_CRAFT) { return this.onClickDrag(cause, slotId, dragType, container); } final Optional slot = container.slot(slotId); @@ -322,7 +322,7 @@ private boolean onClickDrag(final Cause cause, final int slotId, final int dragT } private boolean onClickRight( - final Cause cause, final SlotClickHandler handler, final ClickType clickTypeIn, final Container container, + final Cause cause, final SlotClickHandler handler, final ContainerInput clickTypeIn, final Container container, final int idx, final org.spongepowered.api.item.inventory.Slot slot) { switch (clickTypeIn) { case PICKUP: @@ -338,7 +338,7 @@ private boolean onClickRight( } private Boolean onClickLeft( - final Cause cause, final SlotClickHandler handler, final ClickType clickTypeIn, final Container container, + final Cause cause, final SlotClickHandler handler, final ContainerInput clickTypeIn, final Container container, final int idx, final org.spongepowered.api.item.inventory.Slot slot) { switch (clickTypeIn) { case PICKUP: diff --git a/src/main/java/org/spongepowered/common/inventory/custom/SpongeViewableInventoryBuilder.java b/src/main/java/org/spongepowered/common/inventory/custom/SpongeViewableInventoryBuilder.java index 2f2a7bd8dfb..dc1ad0036b6 100644 --- a/src/main/java/org/spongepowered/common/inventory/custom/SpongeViewableInventoryBuilder.java +++ b/src/main/java/org/spongepowered/common/inventory/custom/SpongeViewableInventoryBuilder.java @@ -25,9 +25,9 @@ package org.spongepowered.common.inventory.custom; import net.minecraft.world.Container; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.animal.horse.AbstractHorse; -import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.EntityTypes; +import net.minecraft.world.entity.animal.equine.AbstractHorse; +import net.minecraft.world.entity.npc.villager.Villager; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AnvilMenu; @@ -379,9 +379,9 @@ public static Map containerTypeInfo() { SpongeViewableInventoryBuilder.register(ContainerTypes.MERCHANT, ContainerTypeInfo.of(sp -> new DefaultEmptyLens(), 0, 0, (id, i, p, vi) -> { - final Villager merchant = new Villager(EntityType.VILLAGER, p.level()); + final Villager merchant = new Villager(EntityTypes.VILLAGER, p.level()); merchant.setPos(p.position()); - merchant.setVillagerData(merchant.getVillagerData().setLevel(5)); + merchant.setVillagerData(merchant.getVillagerData().withLevel(5)); return new MerchantMenu(id, i, vi.applyTradeOffers(merchant)); })); diff --git a/src/main/java/org/spongepowered/common/inventory/custom/ViewableCustomInventory.java b/src/main/java/org/spongepowered/common/inventory/custom/ViewableCustomInventory.java index a62ffd5c089..59ebaf67625 100644 --- a/src/main/java/org/spongepowered/common/inventory/custom/ViewableCustomInventory.java +++ b/src/main/java/org/spongepowered/common/inventory/custom/ViewableCustomInventory.java @@ -76,16 +76,6 @@ public ContainerType getType() { return this.type; } - @Override - public void startOpen(final Player player) { - this.viewers.add(player); // TODO check if this is always called - } - - @Override - public void stopOpen(final Player player) { - this.viewers.remove(player); // TODO check if this is always called - } - @Override public @Nullable AbstractContainerMenu createMenu(int id, net.minecraft.world.entity.player.Inventory playerInv, Player player) { if (this.vanilla) { diff --git a/src/main/java/org/spongepowered/common/inventory/lens/impl/LensRegistrar.java b/src/main/java/org/spongepowered/common/inventory/lens/impl/LensRegistrar.java index 6a9b485b44f..e3822ac0ea5 100644 --- a/src/main/java/org/spongepowered/common/inventory/lens/impl/LensRegistrar.java +++ b/src/main/java/org/spongepowered/common/inventory/lens/impl/LensRegistrar.java @@ -27,10 +27,10 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.world.CompoundContainer; -import net.minecraft.world.entity.npc.AbstractVillager; -import net.minecraft.world.entity.npc.Villager; -import net.minecraft.world.entity.npc.WanderingTrader; -import net.minecraft.world.entity.vehicle.MinecartChest; +import net.minecraft.world.entity.npc.villager.AbstractVillager; +import net.minecraft.world.entity.npc.villager.Villager; +import net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader; +import net.minecraft.world.entity.vehicle.minecart.MinecartChest; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AnvilMenu; import net.minecraft.world.inventory.CraftingContainer; diff --git a/src/main/java/org/spongepowered/common/inventory/lens/impl/comp/CraftingGridInventoryLens.java b/src/main/java/org/spongepowered/common/inventory/lens/impl/comp/CraftingGridInventoryLens.java index 6edb093807f..3dc828381ac 100644 --- a/src/main/java/org/spongepowered/common/inventory/lens/impl/comp/CraftingGridInventoryLens.java +++ b/src/main/java/org/spongepowered/common/inventory/lens/impl/comp/CraftingGridInventoryLens.java @@ -28,7 +28,7 @@ import net.minecraft.world.inventory.InventoryMenu; import org.spongepowered.api.item.inventory.Inventory; import org.spongepowered.api.item.inventory.crafting.CraftingGridInventory; -import org.spongepowered.common.accessor.world.inventory.CraftingMenuAccessor; +import org.spongepowered.common.accessor.world.inventory.AbstractCraftingMenuAccessor; import org.spongepowered.common.inventory.fabric.Fabric; import org.spongepowered.common.inventory.fabric.OffsetFabric; import org.spongepowered.common.inventory.lens.impl.AbstractLens; @@ -70,7 +70,7 @@ public Inventory getAdapter(Fabric fabric, Inventory parent) { return ((CraftingGridInventory) ((InventoryMenu) fabric).getCraftSlots()); } if (fabric instanceof CraftingMenu) { - return ((CraftingGridInventory) ((CraftingMenuAccessor) fabric).accessor$craftSlots()); + return ((CraftingGridInventory) ((AbstractCraftingMenuAccessor) fabric).accessor$craftSlots()); } throw new IllegalStateException(fabric.getClass().getName() + " is not a known CraftingGridInventory."); } diff --git a/src/main/java/org/spongepowered/common/inventory/lens/impl/slot/EquipmentSlotLens.java b/src/main/java/org/spongepowered/common/inventory/lens/impl/slot/EquipmentSlotLens.java index e63fe04826a..c741fa7967d 100644 --- a/src/main/java/org/spongepowered/common/inventory/lens/impl/slot/EquipmentSlotLens.java +++ b/src/main/java/org/spongepowered/common/inventory/lens/impl/slot/EquipmentSlotLens.java @@ -24,8 +24,8 @@ */ package org.spongepowered.common.inventory.lens.impl.slot; +import net.minecraft.core.component.DataComponents; import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.item.Equipable; import org.spongepowered.api.item.inventory.Inventory; import org.spongepowered.api.item.inventory.Slot; import org.spongepowered.api.item.inventory.equipment.EquipmentType; @@ -52,8 +52,8 @@ private static FilteringSlotLens.ItemStackFilter equipmentTypeFilter(EquipmentTy if (item.isEmpty()) { return true; } - final var equipable = Equipable.get(ItemStackUtil.fromLikeToNative(item)); - final var itemSlotType = equipable != null ? equipable.getEquipmentSlot() : EquipmentSlot.MAINHAND; + final var equippable = ItemStackUtil.fromLikeToNative(item).get(DataComponents.EQUIPPABLE); + final var itemSlotType = equippable != null ? equippable.slot() : EquipmentSlot.MAINHAND; return itemSlotType == (Object) type; }; } diff --git a/src/main/java/org/spongepowered/common/inventory/lens/impl/slot/HeldHandSlotLens.java b/src/main/java/org/spongepowered/common/inventory/lens/impl/slot/HeldHandSlotLens.java index 92e0b4ec5eb..ed343baefb0 100644 --- a/src/main/java/org/spongepowered/common/inventory/lens/impl/slot/HeldHandSlotLens.java +++ b/src/main/java/org/spongepowered/common/inventory/lens/impl/slot/HeldHandSlotLens.java @@ -69,20 +69,20 @@ private net.minecraft.world.entity.player.Inventory getInventoryPlayer(Fabric fa @Override public ItemStack getStack(Fabric fabric) { net.minecraft.world.entity.player.Inventory inv = this.getInventoryPlayer(fabric); - return inv.getSelected(); + return inv.getSelectedItem(); } @Override public boolean setStack(Fabric fabric, ItemStack stack) { net.minecraft.world.entity.player.Inventory inv = this.getInventoryPlayer(fabric); - inv.items.set(inv.selected, stack); + inv.setSelectedItem(stack); return true; } @Override public int getOrdinal(Fabric fabric) { net.minecraft.world.entity.player.Inventory inv = this.getInventoryPlayer(fabric); - return inv.selected; + return inv.getSelectedSlot(); } @Override diff --git a/src/main/java/org/spongepowered/common/inventory/util/ContainerUtil.java b/src/main/java/org/spongepowered/common/inventory/util/ContainerUtil.java index 87c3aa91285..480ed8b004e 100644 --- a/src/main/java/org/spongepowered/common/inventory/util/ContainerUtil.java +++ b/src/main/java/org/spongepowered/common/inventory/util/ContainerUtil.java @@ -49,11 +49,11 @@ import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.accessor.world.inventory.AbstractContainerMenuAccessor; import org.spongepowered.common.accessor.world.inventory.AbstractFurnaceMenuAccessor; +import org.spongepowered.common.accessor.world.inventory.AbstractMountInventoryMenuAccessor; import org.spongepowered.common.accessor.world.inventory.BeaconMenuAccessor; import org.spongepowered.common.accessor.world.inventory.BrewingStandMenuAccessor; import org.spongepowered.common.accessor.world.inventory.DispenserMenuAccessor; import org.spongepowered.common.accessor.world.inventory.HopperMenuAccessor; -import org.spongepowered.common.accessor.world.inventory.HorseInventoryMenuAccessor; import org.spongepowered.common.accessor.world.inventory.ItemCombinerMenuAccessor; import org.spongepowered.common.accessor.world.inventory.MerchantMenuAccessor; import org.spongepowered.common.accessor.world.inventory.ResultSlotAccessor; @@ -291,8 +291,8 @@ private static Lens generateAdapterLens(final SlotLensProvider slots, final int return ContainerUtil.carrierOrNull(((BrewingStandMenuAccessor) container).accessor$brewingStand()); } else if (container instanceof BeaconMenu) { return new SpongeBlockEntityCarrier(((BeaconMenuAccessor) container).accessor$access().evaluate(Level::getBlockEntity).orElse(null), container); - } else if (container instanceof HorseInventoryMenuAccessor) { - return (Carrier) ((HorseInventoryMenuAccessor) container).accessor$horse(); + } else if (container instanceof AbstractMountInventoryMenuAccessor) { + return (Carrier) ((AbstractMountInventoryMenuAccessor) container).accessor$mount(); } else if (container instanceof MerchantMenuAccessor && ((MerchantMenuAccessor) container).accessor$trader() instanceof Carrier) { return (Carrier) ((MerchantMenuAccessor) container).accessor$trader(); } else if (container instanceof ItemCombinerMenuAccessor) { diff --git a/src/main/java/org/spongepowered/common/inventory/util/InventoryUtil.java b/src/main/java/org/spongepowered/common/inventory/util/InventoryUtil.java index b9f4d7e1ee0..39c8ba7bf6c 100644 --- a/src/main/java/org/spongepowered/common/inventory/util/InventoryUtil.java +++ b/src/main/java/org/spongepowered/common/inventory/util/InventoryUtil.java @@ -52,9 +52,8 @@ import org.spongepowered.api.item.recipe.crafting.RecipeInput; import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.accessor.world.inventory.CraftingMenuAccessor; +import org.spongepowered.common.accessor.world.inventory.AbstractCraftingMenuAccessor; import org.spongepowered.common.accessor.world.inventory.SmithingMenuAccessor; -import org.spongepowered.common.accessor.world.inventory.StonecutterMenuAccessor; import org.spongepowered.common.bridge.world.inventory.ViewableInventoryBridge; import org.spongepowered.common.bridge.world.inventory.container.TrackedInventoryBridge; import org.spongepowered.common.entity.player.SpongeUserData; @@ -102,9 +101,9 @@ public static Optional toCraftingInput(final Invent case AbstractFurnaceBlockEntity furnace -> new SingleRecipeInput(furnace.getItem(0)); case CampfireBlockEntity campfire -> campfire.getItems().stream().filter(stack -> !stack.isEmpty()).findFirst() .map(SingleRecipeInput::new).orElse(null); - case CraftingMenuAccessor menu -> menu.accessor$craftSlots().asCraftInput(); + case AbstractCraftingMenuAccessor menu -> menu.accessor$craftSlots().asCraftInput(); case InventoryMenu menu -> menu.getCraftSlots().asCraftInput(); - case StonecutterMenu menu -> StonecutterMenuAccessor.invoker$createRecipeInput(menu.container); + case StonecutterMenu menu -> new SingleRecipeInput(menu.container.getItem(0)); case SmithingMenuAccessor menu -> menu.invoker$createRecipeInput(); default -> null; }; @@ -228,7 +227,7 @@ public static void postContainerEvents(final Set containe public static void updateInventoryNoEvents(final Object inventory) { if (inventory instanceof final ViewableInventoryBridge bridge) { - try (final PhaseContext context = BlockPhase.State.RESTORING_BLOCKS.createPhaseContext(PhaseTracker.SERVER)) { + try (final PhaseContext context = BlockPhase.State.RESTORING_BLOCKS.createPhaseContext(PhaseTracker.getInstance())) { context.buildAndSwitch(); for (final ServerPlayer player : bridge.viewableBridge$getViewers()) { player.containerMenu.broadcastChanges(); diff --git a/src/main/java/org/spongepowered/common/ipforward/velocity/VelocityForwardingInfo.java b/src/main/java/org/spongepowered/common/ipforward/velocity/VelocityForwardingInfo.java index 24e61dbee81..99486ad4e48 100644 --- a/src/main/java/org/spongepowered/common/ipforward/velocity/VelocityForwardingInfo.java +++ b/src/main/java/org/spongepowered/common/ipforward/velocity/VelocityForwardingInfo.java @@ -24,9 +24,11 @@ */ package org.spongepowered.common.ipforward.velocity; +import com.google.common.collect.ImmutableMultimap; import com.google.common.net.InetAddresses; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; import io.netty.buffer.ByteBuf; import net.kyori.adventure.text.Component; import net.minecraft.network.Connection; @@ -135,18 +137,20 @@ public static InetAddress readAddress(final ChannelBuf buf) { } public static GameProfile createProfile(final ChannelBuf buf) { - final GameProfile profile = new GameProfile(buf.readUniqueId(), ((FriendlyByteBuf) buf).readUtf(16)); // TODO: ChannelBuf length-limited strings - VelocityForwardingInfo.readProperties(buf, profile); - return profile; + // TODO: ChannelBuf length-limited strings + return new GameProfile(buf.readUniqueId(), ((FriendlyByteBuf) buf).readUtf(16), VelocityForwardingInfo.readProperties(buf)); } - private static void readProperties(final ChannelBuf buf, final GameProfile profile) { + private static PropertyMap readProperties(final ChannelBuf buf) { + final ImmutableMultimap.Builder propertiesBuilder = ImmutableMultimap.builder(); final int properties = buf.readVarInt(); for (int i1 = 0; i1 < properties; i1++) { final String name = buf.readString(); final String value = buf.readString(); final String signature = buf.readBoolean() ? buf.readString() : null; - profile.getProperties().put(name, new Property(name, value, signature)); + propertiesBuilder.put(name, new Property(name, value, signature)); } + final ImmutableMultimap propertiesMap = propertiesBuilder.build(); + return new PropertyMap(propertiesMap); } } diff --git a/src/main/java/org/spongepowered/common/item/ItemStackDataComponentsUpdater.java b/src/main/java/org/spongepowered/common/item/ItemStackDataComponentsUpdater.java index 6406dc8f576..10f3254e669 100644 --- a/src/main/java/org/spongepowered/common/item/ItemStackDataComponentsUpdater.java +++ b/src/main/java/org/spongepowered/common/item/ItemStackDataComponentsUpdater.java @@ -50,6 +50,7 @@ public int outputVersion() { return Constants.ItemStack.Data.DATA_COMPONENTS; } + @SuppressWarnings("deprecation") @Override public DataView update(final DataView content) { final int count = content.getInt(Constants.ItemStack.V2.COUNT).get(); diff --git a/src/main/java/org/spongepowered/common/item/ItemStackSnapshotDuplicateManipulatorUpdater.java b/src/main/java/org/spongepowered/common/item/ItemStackSnapshotDuplicateManipulatorUpdater.java index b9e54ff7435..9ad9eb32c29 100644 --- a/src/main/java/org/spongepowered/common/item/ItemStackSnapshotDuplicateManipulatorUpdater.java +++ b/src/main/java/org/spongepowered/common/item/ItemStackSnapshotDuplicateManipulatorUpdater.java @@ -44,16 +44,13 @@ public int outputVersion() { return Constants.ItemStack.Data.REMOVED_DUPLICATE_DATA; } + @SuppressWarnings("deprecation") @Override public DataView update(DataView content) { if (content.contains(Constants.Sponge.UNSAFE_NBT)) { CompoundTag compound = NBTTranslator.INSTANCE.translate(content.getView(Constants.Sponge.UNSAFE_NBT).get()); - if (compound.contains(Constants.Sponge.Data.V2.SPONGE_DATA)) { - final CompoundTag spongeCompound = compound.getCompound(Constants.Sponge.Data.V2.SPONGE_DATA); - if (spongeCompound.contains(Constants.Sponge.Data.V2.CUSTOM_MANIPULATOR_TAG_LIST)) { - spongeCompound.remove(Constants.Sponge.Data.V2.CUSTOM_MANIPULATOR_TAG_LIST); - } - } + compound.getCompound(Constants.Sponge.Data.V2.SPONGE_DATA) + .ifPresent(c -> c.remove(Constants.Sponge.Data.V2.CUSTOM_MANIPULATOR_TAG_LIST)); Constants.NBT.filterSpongeCustomData(compound); content.remove(Constants.Sponge.UNSAFE_NBT); if (!compound.isEmpty()) { diff --git a/src/main/java/org/spongepowered/common/item/SpongeItemActionFactory.java b/src/main/java/org/spongepowered/common/item/SpongeItemActionFactory.java new file mode 100644 index 00000000000..ce8a0bcc56f --- /dev/null +++ b/src/main/java/org/spongepowered/common/item/SpongeItemActionFactory.java @@ -0,0 +1,93 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.item; + +import net.minecraft.core.HolderSet; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.tags.TagKey; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect; +import net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect; +import net.minecraft.world.item.consume_effects.PlaySoundConsumeEffect; +import net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect; +import net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect; +import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.api.effect.potion.PotionEffect; +import org.spongepowered.api.effect.potion.PotionEffectType; +import org.spongepowered.api.effect.sound.SoundType; +import org.spongepowered.api.tag.Tag; + +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class SpongeItemActionFactory implements ItemAction.Factory { + + @Override + public ItemAction.ApplyEffects applyEffects(final double chance, final List effects) { + Objects.requireNonNull(effects, "effects"); + if (chance < 0 || chance > 1) { + throw new IllegalArgumentException("chance must be in range [0; 1]: " + chance); + } + return (ItemAction.ApplyEffects) (Object) new ApplyStatusEffectsConsumeEffect((List) List.copyOf(effects), (float) chance); + } + + @Override + public ItemAction.RemoveEffects removeEffects(final Set effectTypes) { + Objects.requireNonNull(effectTypes, "effectTypes"); + final var holderSet = HolderSet.direct( + effectType -> BuiltInRegistries.MOB_EFFECT.wrapAsHolder((MobEffect) effectType), + effectTypes); + return (ItemAction.RemoveEffects) (Object) new RemoveStatusEffectsConsumeEffect(holderSet); + } + + @Override + public ItemAction.RemoveEffects removeEffects(final Tag effectTypeTag) { + Objects.requireNonNull(effectTypeTag, "effectTypeTag"); + final var tag = (TagKey) (Object) effectTypeTag; + final var holderSet = BuiltInRegistries.MOB_EFFECT.get(tag).map(hs -> (HolderSet) hs).orElse(HolderSet.empty()); + return (ItemAction.RemoveEffects) (Object) new RemoveStatusEffectsConsumeEffect(holderSet); + } + + @Override + public ItemAction.ClearEffects clearEffects() { + return (ItemAction.ClearEffects) (Object) ClearAllStatusEffectsConsumeEffect.INSTANCE; + } + + @Override + public ItemAction.PlaySound playSound(final SoundType soundType) { + Objects.requireNonNull(soundType, "soundType"); + return (ItemAction.PlaySound) (Object) new PlaySoundConsumeEffect(BuiltInRegistries.SOUND_EVENT.wrapAsHolder((SoundEvent) (Object) soundType)); + } + + @Override + public ItemAction.TeleportRandomly teleportRandomly(final double distance) { + if (distance <= 0) { + throw new IllegalArgumentException("distance must be positive: " + distance); + } + return (ItemAction.TeleportRandomly) (Object) new TeleportRandomlyConsumeEffect((float) distance * 2); + } +} diff --git a/src/main/java/org/spongepowered/common/item/SpongeItemStack.java b/src/main/java/org/spongepowered/common/item/SpongeItemStack.java index 4c0cb5d25cc..5556e6a74b4 100644 --- a/src/main/java/org/spongepowered/common/item/SpongeItemStack.java +++ b/src/main/java/org/spongepowered/common/item/SpongeItemStack.java @@ -28,6 +28,7 @@ import com.google.common.collect.ImmutableList; import com.mojang.serialization.Dynamic; import net.minecraft.SharedConstants; +import net.minecraft.core.Holder; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; @@ -36,7 +37,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.util.datafix.DataFixers; import net.minecraft.util.datafix.fixes.References; import net.minecraft.world.entity.EquipmentSlot; @@ -45,7 +46,9 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.item.component.ItemAttributeModifiers; +import net.minecraft.world.item.component.TypedEntityData; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.NotNull; import org.spongepowered.api.block.BlockSnapshot; @@ -77,7 +80,7 @@ import java.util.Objects; import java.util.Optional; -public final class SpongeItemStack { +public final class SpongeItemStack { public static final class BuilderImpl extends AbstractDataBuilder implements ItemStack.Builder { private ItemType type; @@ -140,11 +143,8 @@ public ItemStack.Builder attributeModifier(final AttributeType attributeType, fi Objects.requireNonNull(modifier, "AttributeModifier cannot be null"); Objects.requireNonNull(equipmentType, "EquipmentType cannot be null"); - var existing = this.components.get(DataComponents.ATTRIBUTE_MODIFIERS); - if (existing == null) { - existing = Optional.empty(); - } - var modifiers = existing.map(ItemAttributeModifiers.class::cast).orElse(ItemAttributeModifiers.EMPTY); + final var existing = Optional.ofNullable(this.components.get(Holder.direct(this.type).components(), DataComponents.ATTRIBUTE_MODIFIERS)); + var modifiers = existing.orElse(ItemAttributeModifiers.EMPTY); var attribute = BuiltInRegistries.ATTRIBUTE.wrapAsHolder((Attribute) attributeType); var slotGroup = SpongeItemStack.asEquipmentSlotGroup(equipmentType); @@ -192,13 +192,20 @@ public ItemStack.Builder fromBlockSnapshot(final BlockSnapshot blockSnapshot) { Objects.requireNonNull(blockSnapshot, "The snapshot was null!"); this.reset(); final BlockType blockType = blockSnapshot.state().type(); - final ResourceLocation blockTypeKey = SpongeCommon.vanillaRegistry(Registries.BLOCK).getKey((Block) blockType); + final Identifier blockTypeKey = SpongeCommon.vanillaRegistry(Registries.BLOCK).getKey((Block) blockType); final Optional itemType = blockType.item(); this.itemType(itemType.orElseThrow(() -> new IllegalArgumentException("ItemType not found for block type: " + blockTypeKey))); this.quantity(1); - if (blockSnapshot instanceof SpongeBlockSnapshot) { - ((SpongeBlockSnapshot) blockSnapshot).getCompound().ifPresent(compoundTag -> { - this.components = DataComponentPatch.builder().set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(compoundTag)).build(); + if (blockSnapshot instanceof SpongeBlockSnapshot sbs) { + sbs.getCompound().ifPresent(compoundTag -> { + final var blockEntityTypes = SpongeCommon.vanillaRegistry(Registries.BLOCK_ENTITY_TYPE); + final var blockEntityType = compoundTag.getString("id") + .map(Identifier::parse) + .flatMap(blockEntityTypes::getOptional); + blockEntityType.ifPresent(beType -> { + TypedEntityData> data = TypedEntityData.of(beType, compoundTag); + this.components = DataComponentPatch.builder().set(DataComponents.BLOCK_ENTITY_DATA, data).build(); + }); }); // todo probably needs more testing, but this'll do donkey... @@ -219,7 +226,7 @@ public ItemStack.Builder fromBlockSnapshot(final BlockSnapshot blockSnapshot) { public ItemStack.Builder fromBlockState(final BlockState blockState) { Objects.requireNonNull(blockState, "blockState"); final BlockType blockType = blockState.type(); - final ResourceLocation blockTypeKey = SpongeCommon.vanillaRegistry(Registries.BLOCK).getKey((Block) blockType); + final Identifier blockTypeKey = SpongeCommon.vanillaRegistry(Registries.BLOCK).getKey((Block) blockType); this.itemType(blockType.item().orElseThrow(() -> new IllegalArgumentException("Missing valid ItemType for BlockType: " + blockTypeKey))); blockState.getValues().forEach(this::add); return this; @@ -276,16 +283,15 @@ public ItemStack empty() { public static DataContainer getDataContainer(final net.minecraft.world.item.ItemStack mcStack) { // Cleanup Old Custom Data SpongeItemStack.cleanupOldCustomData(mcStack); - final CompoundTag stackData = (CompoundTag) mcStack.saveOptional(SpongeCommon.server().registryAccess()); + final CompoundTag stackData = (CompoundTag) net.minecraft.world.item.ItemStack.OPTIONAL_CODEC.encode(mcStack, SpongeCommon.server().registryAccess().createSerializationContext(NbtOps.INSTANCE), new CompoundTag()).getOrThrow(); return DataContainer.createNew() .set(Queries.CONTENT_VERSION, ((ItemStack) (Object) mcStack).contentVersion()) - .set(Constants.ItemStack.V4.DATA_VERSION, SharedConstants.getCurrentVersion().getDataVersion().getVersion()) + .set(Constants.ItemStack.V4.DATA_VERSION, SharedConstants.getCurrentVersion().dataVersion().version()) .set(Constants.ItemStack.V4.DATA, NBTTranslator.INSTANCE.translate(stackData)); } @NotNull public static EquipmentSlotGroup asEquipmentSlotGroup(final EquipmentType equipmentType) { - // TODO expose EquipmentSlotGroup? return switch (((EquipmentSlot) (Object) equipmentType)) { case MAINHAND -> EquipmentSlotGroup.MAINHAND; case OFFHAND -> EquipmentSlotGroup.OFFHAND; @@ -294,14 +300,16 @@ public static EquipmentSlotGroup asEquipmentSlotGroup(final EquipmentType equipm case CHEST -> EquipmentSlotGroup.CHEST; case HEAD -> EquipmentSlotGroup.HEAD; case BODY -> EquipmentSlotGroup.BODY; + case SADDLE -> EquipmentSlotGroup.SADDLE; }; } - + @SuppressWarnings("deprecation") private static void cleanupOldCustomData(final net.minecraft.world.item.ItemStack stack) { - final CompoundTag unsafe = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).getUnsafe(); - unsafe.remove(Constants.Sponge.Data.V2.SPONGE_DATA); // and its V2.CUSTOM_MANIPULATOR_TAG_LIST - Constants.NBT.filterSpongeCustomData(unsafe); // FORGE_DATA > V2.SPONGE_DATA also removes it if empty + stack.update(DataComponents.CUSTOM_DATA, CustomData.EMPTY, data -> data.update(tag -> { + tag.remove(Constants.Sponge.Data.V2.SPONGE_DATA); // and its V2.CUSTOM_MANIPULATOR_TAG_LIST + Constants.NBT.filterSpongeCustomData(tag); // FORGE_DATA > V2.SPONGE_DATA also removes it if empty + })); } // TODO updater for old (maybe V2?) Constants.Sponge.DATA_MANIPULATORS @@ -343,9 +351,9 @@ public static Optional createItemStack(final DataView container) { References.ITEM_STACK, new Dynamic<>(NbtOps.INSTANCE, stackData), dataVersion, - SharedConstants.getCurrentVersion().getDataVersion().getVersion() + SharedConstants.getCurrentVersion().dataVersion().version() ); - return net.minecraft.world.item.ItemStack.parse(SpongeCommon.server().registryAccess(), fixedData.getValue()) - .map(ItemStack.class::cast); + return net.minecraft.world.item.ItemStack.CODEC.parse(SpongeCommon.server().registryAccess().createSerializationContext(NbtOps.INSTANCE), fixedData.getValue()) + .resultOrPartial().map(ItemStack.class::cast); } } diff --git a/src/main/java/org/spongepowered/common/item/SpongeToolRuleFactory.java b/src/main/java/org/spongepowered/common/item/SpongeToolRuleFactory.java index 5850e53f41d..59fc367a080 100644 --- a/src/main/java/org/spongepowered/common/item/SpongeToolRuleFactory.java +++ b/src/main/java/org/spongepowered/common/item/SpongeToolRuleFactory.java @@ -24,15 +24,16 @@ */ package org.spongepowered.common.item; +import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.tags.TagKey; import net.minecraft.world.item.component.Tool; import net.minecraft.world.level.block.Block; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.block.BlockType; import org.spongepowered.api.data.type.ToolRule; import org.spongepowered.api.tag.Tag; +import org.spongepowered.common.bridge.tags.TagBridge; import java.util.List; import java.util.Optional; @@ -41,14 +42,19 @@ public class SpongeToolRuleFactory implements ToolRule.Factory { @Override public ToolRule minesAndDrops(final List blocks, final double speed) { - return (ToolRule) (Object) Tool.Rule.minesAndDrops(blocks.stream().map(Block.class::cast).toList(), (float) speed); + // TODO is Holder::direct allowed here? + final var holderSet = HolderSet.direct(blocks.stream().map(Block.class::cast).map(Holder::direct).toList()); + return (ToolRule) (Object) Tool.Rule.minesAndDrops(holderSet, (float) speed); } @Override public ToolRule minesAndDrops(final Tag blockTypeTag, final double speed) { - return (ToolRule) (Object) Tool.Rule.minesAndDrops((TagKey) (Object) blockTypeTag, (float) speed); + final var tag = ((TagBridge) blockTypeTag).bridge$asVanillaTag(); + final var holderSet = BuiltInRegistries.BLOCK.get(tag).map(hs -> (HolderSet) hs).orElse(HolderSet.empty()); + return (ToolRule) (Object) Tool.Rule.minesAndDrops(holderSet, (float) speed); } + @SuppressWarnings("deprecation") @Override public ToolRule deniesDrops(final List blocks) { // See Tool#forBlocks @@ -58,19 +64,26 @@ public ToolRule deniesDrops(final List blocks) { @Override public ToolRule deniesDrops(final Tag blockTypeTag) { - return (ToolRule) (Object) Tool.Rule.deniesDrops((TagKey) (Object) blockTypeTag); + final var tag = ((TagBridge) blockTypeTag).bridge$asVanillaTag(); + final var holderSet = BuiltInRegistries.BLOCK.get(tag).map(hs -> (HolderSet) hs).orElse(HolderSet.empty()); + return (ToolRule) (Object) Tool.Rule.deniesDrops(holderSet); } + @SuppressWarnings("deprecation") @Override public ToolRule overrideSpeed(final List blocks, final double speed) { - return (ToolRule) (Object) Tool.Rule.overrideSpeed(blocks.stream().map(Block.class::cast).toList(), (float) speed); + final var holderSet = HolderSet.direct(blocks.stream().map(Block.class::cast).map(Block::builtInRegistryHolder).toList()); + return (ToolRule) (Object) Tool.Rule.overrideSpeed(holderSet, (float) speed); } @Override public ToolRule overrideSpeed(final Tag blockTypeTag, final double speed) { - return (ToolRule) (Object) Tool.Rule.overrideSpeed((TagKey) (Object) blockTypeTag, (float) speed); + final var tag = ((TagBridge) blockTypeTag).bridge$asVanillaTag(); + final var holderSet = BuiltInRegistries.BLOCK.get(tag).map(hs -> (HolderSet) hs).orElse(HolderSet.empty()); + return (ToolRule) (Object) Tool.Rule.overrideSpeed(holderSet, (float) speed); } + @SuppressWarnings("deprecation") @Override public ToolRule forBlocks(final List blocks, @Nullable final Double speed, @Nullable final Boolean drops) { // See Tool#forBlocks @@ -81,7 +94,8 @@ public ToolRule forBlocks(final List blocks, @Nullable final Double s @Override public ToolRule forTag(final Tag blockTypeTag, @Nullable final Double speed, @Nullable final Boolean drops) { // See Tool#forTag - final var holderSet = BuiltInRegistries.BLOCK.getOrCreateTag((TagKey) (Object) blockTypeTag); + final var tag = ((TagBridge) blockTypeTag).bridge$asVanillaTag(); + final var holderSet = BuiltInRegistries.BLOCK.get(tag).map(hs -> (HolderSet) hs).orElse(HolderSet.empty()); return (ToolRule) (Object) new Tool.Rule(holderSet, Optional.ofNullable(speed).map(Double::floatValue), Optional.ofNullable(drops)); } } diff --git a/src/main/java/org/spongepowered/common/item/enchantment/SpongeEnchantment.java b/src/main/java/org/spongepowered/common/item/enchantment/SpongeEnchantment.java index c708fff2c70..daa623d4f6c 100644 --- a/src/main/java/org/spongepowered/common/item/enchantment/SpongeEnchantment.java +++ b/src/main/java/org/spongepowered/common/item/enchantment/SpongeEnchantment.java @@ -24,17 +24,22 @@ */ package org.spongepowered.common.item.enchantment; +import net.kyori.adventure.text.Component; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.world.item.enchantment.Enchantment; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.data.persistence.Queries; -import org.spongepowered.api.item.enchantment.Enchantment; import org.spongepowered.api.item.enchantment.EnchantmentType; import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.adventure.SpongeAdventure; import java.util.Objects; -public final class SpongeEnchantment implements Enchantment { +public final class SpongeEnchantment implements org.spongepowered.api.item.enchantment.Enchantment { private final EnchantmentType enchantmentType; private final int level; @@ -54,6 +59,13 @@ public int level() { return this.level; } + @Override + public Component asComponent() { + final Registry registry = (Registry) SpongeCommon.scopedHolder().registry(RegistryTypes.ENCHANTMENT_TYPE); + final Holder holder = registry.wrapAsHolder((Enchantment) (Object) this.enchantmentType); + return SpongeAdventure.asAdventure(Enchantment.getFullname(holder, this.level)); + } + @Override public int contentVersion() { return 1; diff --git a/src/main/java/org/spongepowered/common/item/enchantment/SpongeRandomEnchantmentListBuilder.java b/src/main/java/org/spongepowered/common/item/enchantment/SpongeRandomEnchantmentListBuilder.java index 1c8c25e8881..d7c5373d353 100644 --- a/src/main/java/org/spongepowered/common/item/enchantment/SpongeRandomEnchantmentListBuilder.java +++ b/src/main/java/org/spongepowered/common/item/enchantment/SpongeRandomEnchantmentListBuilder.java @@ -26,7 +26,6 @@ import com.google.common.collect.Lists; -import net.minecraft.Util; import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; import net.minecraft.util.RandomSource; @@ -93,8 +92,8 @@ public List build() throws IllegalStateException { if (this.pool == null || this.pool.isEmpty()) { Objects.requireNonNull(this.item, "The item cannot be null"); this.randomSource.setSeed(this.seed + this.option); - final var registry = SpongeCommon.server().registryAccess().registryOrThrow(Registries.ENCHANTMENT); - var stream = registry.holders().map($$0x -> (Holder)$$0x); + final var registry = SpongeCommon.vanillaRegistry(Registries.ENCHANTMENT); + var stream = registry.listElements().map($$0x -> (Holder)$$0x); enchantments = EnchantmentHelper.selectEnchantment(this.randomSource, ItemStackUtil.toNative(this.item), this.level, stream); } else { this.randomSource.setSeed(this.seed + this.option); @@ -115,18 +114,18 @@ private List basedOfFixedPool(final RandomSource randomIn, // Same code as net.minecraft.enchantment.EnchantmentHelper#selectEnchantment if (!list1.isEmpty()) { - WeightedRandom.getRandomItem(randomIn, list1).ifPresent(list::add); + WeightedRandom.getRandomItem(randomIn, list1, EnchantmentInstance::weight).ifPresent(list::add); while (randomIn.nextInt(50) <= this.level) { - EnchantmentHelper.filterCompatibleEnchantments(list1, Util.lastOf(list)); + EnchantmentHelper.filterCompatibleEnchantments(list1, list.getLast()); if (list1.isEmpty()) { break; } - WeightedRandom.getRandomItem(randomIn, list1).ifPresent(list::add); + WeightedRandom.getRandomItem(randomIn, list1, EnchantmentInstance::weight).ifPresent(list::add); this.level /= 2; } } @@ -135,11 +134,11 @@ private List basedOfFixedPool(final RandomSource randomIn, } public static List fromNative(final List list) { - return list.stream().map(data -> Enchantment.of(((EnchantmentType) (Object) data.enchantment.value()), data.level)).collect(Collectors.toList()); + return list.stream().map(data -> Enchantment.of(((EnchantmentType) (Object) data.enchantment().value()), data.level())).collect(Collectors.toList()); } public static List toNative(final List list) { - final var registry = SpongeCommon.server().registryAccess().registryOrThrow(Registries.ENCHANTMENT); + final var registry = SpongeCommon.vanillaRegistry(Registries.ENCHANTMENT); return list.stream().map(ench -> { var mcEnch = registry.wrapAsHolder((net.minecraft.world.item.enchantment.Enchantment) (Object) ench.type()); return new EnchantmentInstance(mcEnch, ench.level()); diff --git a/src/main/java/org/spongepowered/common/item/merchant/SpongeTradeOfferBuilder.java b/src/main/java/org/spongepowered/common/item/merchant/SpongeTradeOfferBuilder.java index c189fd8f13d..24290f215bc 100644 --- a/src/main/java/org/spongepowered/common/item/merchant/SpongeTradeOfferBuilder.java +++ b/src/main/java/org/spongepowered/common/item/merchant/SpongeTradeOfferBuilder.java @@ -25,7 +25,7 @@ package org.spongepowered.common.item.merchant; -import net.minecraft.core.component.DataComponentPredicate; +import net.minecraft.core.component.DataComponentExactPredicate; import net.minecraft.world.item.trading.ItemCost; import net.minecraft.world.item.trading.MerchantOffer; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -146,7 +146,7 @@ public TradeOffer build() throws IllegalStateException { @NotNull private static ItemCost itemCostOf(final net.minecraft.world.item.ItemStack stack) { - return new ItemCost(stack.getItemHolder(), stack.getCount(), DataComponentPredicate.allOf(stack.getComponents()), stack); + return new ItemCost(stack.typeHolder(), stack.getCount(), DataComponentExactPredicate.allOf(stack.getComponents()), stack); } @Override diff --git a/src/main/java/org/spongepowered/common/item/merchant/TradeOfferGeneratorWrapper.java b/src/main/java/org/spongepowered/common/item/merchant/TradeOfferGeneratorWrapper.java index c5368a64499..6a01d5e3777 100644 --- a/src/main/java/org/spongepowered/common/item/merchant/TradeOfferGeneratorWrapper.java +++ b/src/main/java/org/spongepowered/common/item/merchant/TradeOfferGeneratorWrapper.java @@ -24,10 +24,6 @@ */ package org.spongepowered.common.item.merchant; -import net.minecraft.util.RandomSource; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.npc.VillagerTrades; -import net.minecraft.world.item.trading.MerchantOffer; import org.spongepowered.api.item.merchant.TradeOffer; import org.spongepowered.api.item.merchant.TradeOfferGenerator; import org.spongepowered.api.util.RandomProvider; @@ -35,7 +31,7 @@ import java.util.Objects; import java.util.StringJoiner; -public final class TradeOfferGeneratorWrapper implements VillagerTrades.ItemListing, TradeOfferGenerator { +public final class TradeOfferGeneratorWrapper implements TradeOfferGenerator { private final TradeOfferGenerator generator; @@ -43,11 +39,6 @@ public TradeOfferGeneratorWrapper(final TradeOfferGenerator generator) { this.generator = generator; } - @Override - public MerchantOffer getOffer(final Entity trader, final RandomSource rand) { - return (MerchantOffer) this.generator.apply((org.spongepowered.api.entity.Entity) trader, (RandomProvider.Source) rand); - } - @Override public TradeOffer apply(final org.spongepowered.api.entity.Entity entity, final RandomProvider.Source random) { return this.generator.apply(entity, random); @@ -80,4 +71,5 @@ public String toString() { .add("generator=" + this.generator) .toString(); } + } diff --git a/src/main/java/org/spongepowered/common/item/recipe/SpongeRecipeRegistration.java b/src/main/java/org/spongepowered/common/item/recipe/SpongeRecipeRegistration.java index 7688025bf6a..8fee718ae2d 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/SpongeRecipeRegistration.java +++ b/src/main/java/org/spongepowered/common/item/recipe/SpongeRecipeRegistration.java @@ -24,154 +24,25 @@ */ package org.spongepowered.common.item.recipe; -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.JsonOps; -import net.minecraft.advancements.Advancement; -import net.minecraft.advancements.AdvancementHolder; -import net.minecraft.advancements.AdvancementRewards; -import net.minecraft.advancements.critereon.RecipeUnlockedTrigger; import net.minecraft.core.NonNullList; -import net.minecraft.core.RegistryAccess; -import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.RegistryOps; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeInput; -import net.minecraft.world.item.crafting.RecipeSerializer; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.item.recipe.RecipeRegistration; -import org.spongepowered.common.item.recipe.ingredient.IngredientResultUtil; import org.spongepowered.common.item.recipe.ingredient.SpongeIngredient; -import java.io.IOException; import java.util.Collection; import java.util.function.Function; -public abstract class SpongeRecipeRegistration> implements RecipeRegistration { +public final class SpongeRecipeRegistration { - private static final Gson GSON = new Gson(); - - protected final ResourceLocation key; - protected final RecipeSerializer serializer; - protected final AdvancementHolder advancement; - protected final String group; - protected final DataPack pack; - - public SpongeRecipeRegistration(final ResourceLocation key, - final String group, final DataPack pack, final RecipeCategory recipeCategory, - final RecipeSerializer serializer) { - this.key = key; - this.serializer = serializer; - this.pack = pack; - this.advancement = Advancement.Builder.advancement() - .addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(key)) - .rewards(AdvancementRewards.Builder.recipe(key)) - .build(ResourceLocation.fromNamespaceAndPath(key.getNamespace(), "recipes/" + recipeCategory.getFolderName() + "/" + key.getPath())); - this.group = group == null ? "" : group; - } - - public static , I extends RecipeInput> RecipeSerializer determineSerializer(final ItemStack resultStack, - final Function resultFunction, - final Function> remainingItemsFunction, - final Collection ingredients, - final RecipeSerializer vanilla, final RecipeSerializer sponge) { - if (!resultStack.getComponents().isEmpty() || resultFunction != null || remainingItemsFunction != null) { - return sponge; - } - for (final Ingredient value : ingredients) { - if (value instanceof SpongeIngredient) { - return sponge; - } - } - return vanilla; - } - - public static boolean isVanillaSerializer(final ItemStack resultStack, - final Function resultFunction, - final Function> remainingItemsFunction, - final Collection ingredients) { - if (!resultStack.getComponents().isEmpty() || resultFunction != null || remainingItemsFunction != null) { + public static boolean isVanillaSerializer(final ItemStackTemplate resultStack, + final Function resultFunction, + final Function> remainingItemsFunction, + final Collection ingredients) { + if (!resultStack.components().isEmpty() || resultFunction != null || remainingItemsFunction != null) { return false; } - for (final Ingredient value : ingredients) { - if (value instanceof SpongeIngredient) { - return false; - } - } - return true; - } - - @Override - public ResourceKey key() { - return (ResourceKey) (Object) this.key; - } - - @Override - public int contentVersion() { - return 1; - } - - @Override - public DataContainer toContainer() { - var json = Recipe.CODEC.encodeStart(JsonOps.INSTANCE, (Recipe) this.recipe()).result().get(); - try { - return DataFormats.JSON.get().read(GSON.toJson(json)); // TODO serialize or get DataContainer without serializing - } catch (final IOException e) { - throw new IllegalStateException(e); - } - } - - @Override - public DataPack pack() { - return this.pack; - } - - @SuppressWarnings("unchecked") - protected void ensureCached() { - if (this instanceof SpongeRecipeRegistration.ResultFunctionRegistration rfr) { - IngredientResultUtil.cacheResultFunction(this.key, (Function) rfr.resultFunction()); - } - if (this instanceof SpongeRecipeRegistration.RemainingItemsFunctionRegistration rifr) { - IngredientResultUtil.cacheRemainingItemsFunction(this.key, (Function) rifr.remainingItems()); - } - } - - public AdvancementHolder advancement() { - return advancement; - } - - public static JsonObject encode(RecipeRegistration template, RegistryAccess access) { - try { - if (template instanceof SpongeRecipeRegistration srr) { - srr.ensureCached(); - } - final var ops = RegistryOps.create(JsonOps.INSTANCE, access); - final DataResult encoded = Recipe.CODEC.encodeStart(ops, (Recipe) template.recipe()); - if (encoded.result().isPresent()) { - return encoded.result().get().getAsJsonObject(); - } - final var error = encoded.error().get(); - throw new RuntimeException(error.message()); - } catch (Exception e) { - throw new RuntimeException("Could not encode recipe " + template.key(), e); - } - } - - public interface ResultFunctionRegistration { - - Function resultFunction(); - } - - public interface RemainingItemsFunctionRegistration { - - Function> remainingItems(); + return ingredients.stream().noneMatch(value -> value instanceof SpongeIngredient); } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeBlastingRecipe.java b/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeBlastingRecipe.java index 6675d43b475..d22500345c5 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeBlastingRecipe.java +++ b/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeBlastingRecipe.java @@ -24,47 +24,41 @@ */ package org.spongepowered.common.item.recipe.cooking; -import net.minecraft.core.HolderLookup; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.BlastingRecipe; import net.minecraft.world.item.crafting.CookingBookCategory; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.SingleRecipeInput; -import org.spongepowered.common.item.recipe.ResultFunctionRecipe; -import org.spongepowered.common.item.recipe.ingredient.IngredientResultUtil; -import java.util.Optional; +import java.util.function.Function; -public class SpongeBlastingRecipe extends BlastingRecipe implements ResultFunctionRecipe { +public class SpongeBlastingRecipe extends BlastingRecipe { - private final String resultFunctionId; + private final Function function; - public SpongeBlastingRecipe(final String group, final CookingBookCategory category, final Ingredient ingredient, final ItemStack result, final float experience, final int cookingTime, final String resultFunctionId) { - super(group, category, ingredient, result, experience, cookingTime); - this.resultFunctionId = resultFunctionId; + public SpongeBlastingRecipe( + final String group, + final CookingBookCategory category, + final Ingredient ingredient, + final ItemStackTemplate result, + final float experience, + final int cookingTime, + final Function function + ) { + super(new CommonInfo(false), new CookingBookInfo(category, group), ingredient, result, experience, cookingTime); + this.function = function; } @Override - public Optional resultFunctionId() { - return Optional.ofNullable(this.resultFunctionId); - } - - @Override - public ItemStack assemble(final SingleRecipeInput $$0, final HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - final ItemStack result = IngredientResultUtil.cachedResultFunction(this.resultFunctionId).apply($$0); + public ItemStack assemble(final SingleRecipeInput input) { + if (this.function != null) { + final ItemStack result = this.function.apply(input); result.setCount(1); return result; } - return super.assemble($$0, $$1); + return super.assemble(input); } - @Override - public ItemStack getResultItem(final HolderLookup.Provider $$1) { -// if (this.resultFunction != null) { -// return ItemStack.EMPTY; -// } - return super.getResultItem($$1); - } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeCampfireCookingRecipe.java b/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeCampfireCookingRecipe.java index ecce010f0ab..19797584844 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeCampfireCookingRecipe.java +++ b/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeCampfireCookingRecipe.java @@ -24,47 +24,40 @@ */ package org.spongepowered.common.item.recipe.cooking; -import net.minecraft.core.HolderLookup; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.CampfireCookingRecipe; import net.minecraft.world.item.crafting.CookingBookCategory; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.SingleRecipeInput; -import org.spongepowered.common.item.recipe.ResultFunctionRecipe; -import org.spongepowered.common.item.recipe.ingredient.IngredientResultUtil; -import java.util.Optional; +import java.util.function.Function; -public class SpongeCampfireCookingRecipe extends CampfireCookingRecipe implements ResultFunctionRecipe { +public class SpongeCampfireCookingRecipe extends CampfireCookingRecipe { - private final String resultFunctionId; + private final Function function; - public SpongeCampfireCookingRecipe(final String group, final CookingBookCategory category, final Ingredient ingredient, final ItemStack result, final float experience, final int cookingTime, final String resultFunctionId) { - super(group, category, ingredient, result, experience, cookingTime); - this.resultFunctionId = resultFunctionId; + public SpongeCampfireCookingRecipe( + final String group, + final CookingBookCategory category, + final Ingredient ingredient, + final ItemStackTemplate result, + final float experience, + final int cookingTime, + final Function function + ) { + super(new CommonInfo(false), new CookingBookInfo(category, group), ingredient, result, experience, cookingTime); + this.function = function; } @Override - public Optional resultFunctionId() { - return Optional.ofNullable(this.resultFunctionId); - } - - @Override - public ItemStack assemble(final SingleRecipeInput $$0, final HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - final ItemStack result = IngredientResultUtil.cachedResultFunction(this.resultFunctionId).apply($$0); + public ItemStack assemble(final SingleRecipeInput $$0) { + if (this.function != null) { + final ItemStack result = this.function.apply($$0); result.setCount(1); return result; } - return super.assemble($$0, $$1); - } - - @Override - public ItemStack getResultItem(final HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - return ItemStack.EMPTY; - } - return super.getResultItem($$1); + return super.assemble($$0); } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeCookingRecipeBuilder.java b/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeCookingRecipeBuilder.java index d173ba4a708..ed4b2cfa2e1 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeCookingRecipeBuilder.java +++ b/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeCookingRecipeBuilder.java @@ -25,41 +25,43 @@ package org.spongepowered.common.item.recipe.cooking; import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; +import net.minecraft.world.item.crafting.AbstractCookingRecipe; +import net.minecraft.world.item.crafting.BlastingRecipe; +import net.minecraft.world.item.crafting.CampfireCookingRecipe; import net.minecraft.world.item.crafting.CookingBookCategory; import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.SingleRecipeInput; +import net.minecraft.world.item.crafting.SmeltingRecipe; +import net.minecraft.world.item.crafting.SmokingRecipe; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.item.ItemType; import org.spongepowered.api.item.inventory.ItemStackLike; -import org.spongepowered.api.item.recipe.RecipeRegistration; import org.spongepowered.api.item.recipe.RecipeType; import org.spongepowered.api.item.recipe.cooking.CookingRecipe; import org.spongepowered.api.item.recipe.crafting.RecipeInput; import org.spongepowered.api.util.Ticks; import org.spongepowered.common.inventory.util.InventoryUtil; +import org.spongepowered.common.item.recipe.SpongeRecipeRegistration; import org.spongepowered.common.item.util.ItemStackUtil; -import org.spongepowered.common.util.AbstractResourceKeyedBuilder; +import org.spongepowered.common.util.SpongeTicks; +import java.util.Collections; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; -public final class SpongeCookingRecipeBuilder extends AbstractResourceKeyedBuilder - implements CookingRecipe.Builder.ResultStep, CookingRecipe.Builder.IngredientStep, CookingRecipe.Builder.EndStep { +public final class SpongeCookingRecipeBuilder implements CookingRecipe.Builder.ResultStep, CookingRecipe.Builder.IngredientStep, CookingRecipe.Builder.EndStep { private net.minecraft.world.item.crafting.RecipeType type; private Ingredient ingredient; - private ItemStack result; + private ItemStackTemplate result; private Function resultFunction; private @Nullable Float experience; private @Nullable Ticks cookingTime; private @Nullable String group; - private DataPack pack = DataPacks.RECIPE; private RecipeCategory recipeCategory = RecipeCategory.MISC; // TODO support category private CookingBookCategory cookingCategory = CookingBookCategory.MISC; // TODO support category @@ -72,7 +74,6 @@ public ResultStep ingredient(final org.spongepowered.api.item.recipe.crafting.In @Override public CookingRecipe.Builder reset() { - super.reset(); this.type = null; this.ingredient = null; this.result = null; @@ -80,27 +81,26 @@ public CookingRecipe.Builder reset() { this.experience = null; this.cookingTime = null; this.group = null; - this.pack = DataPacks.RECIPE; return this; } @Override public EndStep result(final ItemType result) { - this.result = new ItemStack((Item) result); + this.result = ItemStackUtil.toTemplate(org.spongepowered.api.item.inventory.ItemStack.of(result)); this.resultFunction = null; return this; } @Override public EndStep result(final ItemStackLike result) { - this.result = ItemStackUtil.fromLikeToNative(result); + this.result = ItemStackUtil.toTemplate(result); this.resultFunction = null; return this; } // currently unused public EndStep result(final Function resultFunction, final ItemStackLike exemplaryResult) { - this.result = ItemStackUtil.fromLikeToNative(exemplaryResult); + this.result = ItemStackUtil.toTemplate(exemplaryResult); this.resultFunction = (inv) -> ItemStackUtil.fromLikeToNative(resultFunction.apply(InventoryUtil.toSponge(inv))); return this; } @@ -133,28 +133,45 @@ public EndStep group(final String group) { } @Override - public EndStep pack(final DataPack pack) { - this.pack = pack; - return this; - } - - @Override - protected RecipeRegistration build0() { + public CookingRecipe build() { Objects.requireNonNull(this.type, "type"); Objects.requireNonNull(this.ingredient, "ingredient"); Objects.requireNonNull(this.result, "result"); - return SpongeCookingRecipeRegistration.of((ResourceLocation) (Object) this.key, - this.type, - this.group, - this.ingredient, - this.experience, - this.cookingTime, - this.result, - this.resultFunction, - this.pack, - this.recipeCategory, - this.cookingCategory); - } + final var ingredientList = Collections.singletonList(this.ingredient); + final boolean isVanilla = SpongeRecipeRegistration.isVanillaSerializer(this.result, this.resultFunction, null, ingredientList); + + String group = this.group == null ? "" : this.group; + float experience = this.experience == null ? 0 : this.experience; + if (this.type == net.minecraft.world.item.crafting.RecipeType.BLASTING) { + final int ticksCookingTime = Optional.ofNullable(this.cookingTime).map(SpongeTicks::toSaturatedIntOrInfinite).orElse(100); + if (!isVanilla) { + return (CookingRecipe) new SpongeBlastingRecipe(group, this.cookingCategory, this.ingredient, this.result, experience, ticksCookingTime, this.resultFunction); + } + return (CookingRecipe) new BlastingRecipe(new Recipe.CommonInfo(false), new AbstractCookingRecipe.CookingBookInfo(this.cookingCategory, group), this.ingredient, this.result, experience, ticksCookingTime); + } + if (this.type == net.minecraft.world.item.crafting.RecipeType.CAMPFIRE_COOKING) { + final int ticksCookingTime = Optional.ofNullable(this.cookingTime).map(SpongeTicks::toSaturatedIntOrInfinite).orElse(600); + if (!isVanilla) { + return (CookingRecipe) new SpongeCampfireCookingRecipe(group, this.cookingCategory, this.ingredient, this.result, experience, ticksCookingTime, this.resultFunction); + } + return (CookingRecipe) new CampfireCookingRecipe(new Recipe.CommonInfo(false), new AbstractCookingRecipe.CookingBookInfo(this.cookingCategory, group), this.ingredient, this.result, experience, ticksCookingTime); + } + if (this.type == net.minecraft.world.item.crafting.RecipeType.SMOKING) { + final int ticksCookingTime = Optional.ofNullable(this.cookingTime).map(SpongeTicks::toSaturatedIntOrInfinite).orElse(100); + if (!isVanilla) { + return (CookingRecipe) new SpongeSmokingRecipe(group, this.cookingCategory, this.ingredient, this.result, experience, ticksCookingTime, this.resultFunction); + } + return (CookingRecipe) new SmokingRecipe(new Recipe.CommonInfo(false), new AbstractCookingRecipe.CookingBookInfo(this.cookingCategory, group), this.ingredient, this.result, experience, ticksCookingTime); + } + if (this.type == net.minecraft.world.item.crafting.RecipeType.SMELTING) { + final int ticksCookingTime = Optional.ofNullable(this.cookingTime).map(SpongeTicks::toSaturatedIntOrInfinite).orElse(200); + if (!isVanilla) { + return (CookingRecipe) new SpongeSmeltingRecipe(group, this.cookingCategory, this.ingredient, this.result, experience, ticksCookingTime, this.resultFunction); + } + return (CookingRecipe) new SmeltingRecipe(new Recipe.CommonInfo(false), new AbstractCookingRecipe.CookingBookInfo(this.cookingCategory, group), this.ingredient, this.result, experience, ticksCookingTime); + } + throw new IllegalArgumentException("Unknown RecipeType " + this.type); + } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeCookingRecipeRegistration.java b/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeCookingRecipeRegistration.java deleted file mode 100644 index 040c5be9642..00000000000 --- a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeCookingRecipeRegistration.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.item.recipe.cooking; - -import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.AbstractCookingRecipe; -import net.minecraft.world.item.crafting.BlastingRecipe; -import net.minecraft.world.item.crafting.CampfireCookingRecipe; -import net.minecraft.world.item.crafting.CookingBookCategory; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.RecipeSerializer; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.item.crafting.SingleRecipeInput; -import net.minecraft.world.item.crafting.SmeltingRecipe; -import net.minecraft.world.item.crafting.SmokingRecipe; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.item.recipe.Recipe; -import org.spongepowered.api.item.recipe.RecipeRegistration; -import org.spongepowered.api.item.recipe.cooking.CookingRecipe; -import org.spongepowered.api.util.Ticks; -import org.spongepowered.common.item.recipe.SpongeRecipeRegistration; -import org.spongepowered.common.util.SpongeTicks; - -import java.util.Collections; -import java.util.List; -import java.util.function.Function; - -public class SpongeCookingRecipeRegistration extends SpongeRecipeRegistration implements SpongeRecipeRegistration.ResultFunctionRegistration{ - - // Vanilla Recipe - private final Ingredient ingredient; - private final float experience; - private final Ticks cookingTime; - - // Sponge Recipe - private final ItemStack spongeResult; - private final Function resultFunction; - private final RecipeType type; - private final CookingBookCategory cookingCategory; - - public SpongeCookingRecipeRegistration(final ResourceLocation key, final RecipeType type, final RecipeSerializer serializer, - final String group, final Ingredient ingredient, final float experience, final Ticks cookingTime, - final ItemStack spongeResult, final Function resultFunction, - final DataPack pack, final RecipeCategory category, final CookingBookCategory cookingCategory) { - super(key, group, pack, category, serializer); - this.type = type; - this.ingredient = ingredient; - this.experience = experience; - this.cookingTime = cookingTime; - this.spongeResult = spongeResult; - this.resultFunction = resultFunction; - this.cookingCategory = cookingCategory; - } - - public static SpongeCookingRecipeRegistration of(final ResourceLocation key, final RecipeType type, final @Nullable String group, - final Ingredient ingredient, final Float experience, final @Nullable Ticks cookingTime, final ItemStack result, - final Function resultFunction, final DataPack pack, final RecipeCategory recipeCategory, final CookingBookCategory cookingCategory) - { - var finalCookingTime = cookingTime; - - final RecipeSerializer serializer; - if (type == RecipeType.BLASTING) { - if (finalCookingTime == null) { - finalCookingTime = Ticks.of(100); - } - serializer = RecipeSerializer.BLASTING_RECIPE; - } else if (type == RecipeType.CAMPFIRE_COOKING) { - if (finalCookingTime == null) { - finalCookingTime = Ticks.of(600); - } - serializer = RecipeSerializer.CAMPFIRE_COOKING_RECIPE; - } else if (type == RecipeType.SMOKING) { - if (finalCookingTime == null) { - finalCookingTime = Ticks.of(100); - } - serializer = RecipeSerializer.SMOKING_RECIPE; - } else if (type == RecipeType.SMELTING) { - if (finalCookingTime == null) { - finalCookingTime = Ticks.of(200); - } - serializer = RecipeSerializer.SMELTING_RECIPE; - } else { - throw new IllegalArgumentException("Unknown RecipeType " + type); - } - - return new SpongeCookingRecipeRegistration(key, type, serializer, group, - ingredient, experience == null ? 0 : experience, finalCookingTime, result, resultFunction, pack, recipeCategory, cookingCategory); - } - - @Override - public Recipe recipe() { - this.ensureCached(); - final int ticksCookingTime = SpongeTicks.toSaturatedIntOrInfinite(this.cookingTime); - final String resultFunctionId = this.resultFunction == null ? null : this.key.toString(); - - final List ingredientList = Collections.singletonList(ingredient); - final boolean isVanilla = SpongeRecipeRegistration.isVanillaSerializer(this.spongeResult, this.resultFunction, null, ingredientList); - - if (type == RecipeType.BLASTING) { - if (!isVanilla) { - return (CookingRecipe) new SpongeBlastingRecipe(this.group, this.cookingCategory, this.ingredient, this.spongeResult, this.experience, ticksCookingTime, resultFunctionId); - } - return (CookingRecipe) new BlastingRecipe(this.group, this.cookingCategory, this.ingredient, this.spongeResult, this.experience, ticksCookingTime); - } - if (type == RecipeType.CAMPFIRE_COOKING) { - if (!isVanilla) { - return (CookingRecipe) new SpongeCampfireCookingRecipe(this.group, this.cookingCategory, this.ingredient, this.spongeResult, this.experience, ticksCookingTime, resultFunctionId); - } - return (CookingRecipe) new CampfireCookingRecipe(this.group, this.cookingCategory, this.ingredient, this.spongeResult, this.experience, ticksCookingTime); - } - if (type == RecipeType.SMOKING) { - if (!isVanilla) { - return (CookingRecipe) new SpongeSmokingRecipe(this.group, this.cookingCategory, this.ingredient, this.spongeResult, this.experience, ticksCookingTime, resultFunctionId); - } - return (CookingRecipe) new SmokingRecipe(this.group, this.cookingCategory, this.ingredient, this.spongeResult, this.experience, ticksCookingTime); - } - if (type == RecipeType.SMELTING) { - if (!isVanilla) { - return (CookingRecipe) new SpongeSmeltingRecipe(this.group, this.cookingCategory, this.ingredient, this.spongeResult, this.experience, ticksCookingTime, resultFunctionId); - } - return (CookingRecipe) new SmeltingRecipe(this.group, this.cookingCategory, this.ingredient, this.spongeResult, this.experience, ticksCookingTime); - } - throw new IllegalArgumentException("Unknown RecipeType " + type); - - - } - - @Override - public Function resultFunction() { - return this.resultFunction; - } -} diff --git a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeSmeltingRecipe.java b/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeSmeltingRecipe.java index 13e51a37ac7..c95aee6ac37 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeSmeltingRecipe.java +++ b/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeSmeltingRecipe.java @@ -24,47 +24,40 @@ */ package org.spongepowered.common.item.recipe.cooking; -import net.minecraft.core.HolderLookup; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.CookingBookCategory; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.SingleRecipeInput; import net.minecraft.world.item.crafting.SmeltingRecipe; -import org.spongepowered.common.item.recipe.ResultFunctionRecipe; -import org.spongepowered.common.item.recipe.ingredient.IngredientResultUtil; -import java.util.Optional; +import java.util.function.Function; -public class SpongeSmeltingRecipe extends SmeltingRecipe implements ResultFunctionRecipe { +public class SpongeSmeltingRecipe extends SmeltingRecipe { - private final String resultFunctionId; + private final Function function; - public SpongeSmeltingRecipe(final String group, final CookingBookCategory category, final Ingredient ingredient, final ItemStack result, final float experience, final int cookingTime, final String resultFunctionId) { - super(group, category, ingredient, result, experience, cookingTime); - this.resultFunctionId = resultFunctionId; + public SpongeSmeltingRecipe( + final String group, + final CookingBookCategory category, + final Ingredient ingredient, + final ItemStackTemplate result, + final float experience, + final int cookingTime, + final Function function + ) { + super(new CommonInfo(false), new CookingBookInfo(category, group), ingredient, result, experience, cookingTime); + this.function = function; } @Override - public Optional resultFunctionId() { - return Optional.ofNullable(this.resultFunctionId); - } - - @Override - public ItemStack assemble(final SingleRecipeInput $$0, final HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - final ItemStack result = IngredientResultUtil.cachedResultFunction(this.resultFunctionId).apply($$0); + public ItemStack assemble(final SingleRecipeInput input) { + if (this.function != null) { + final ItemStack result = this.function.apply(input); result.setCount(1); return result; } - return super.assemble($$0, $$1); - } - - @Override - public ItemStack getResultItem(final HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - return ItemStack.EMPTY; - } - return super.getResultItem($$1); + return super.assemble(input); } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeSmokingRecipe.java b/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeSmokingRecipe.java index 5397cad2138..138bc51e24c 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeSmokingRecipe.java +++ b/src/main/java/org/spongepowered/common/item/recipe/cooking/SpongeSmokingRecipe.java @@ -24,47 +24,40 @@ */ package org.spongepowered.common.item.recipe.cooking; -import net.minecraft.core.HolderLookup; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.CookingBookCategory; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.SingleRecipeInput; import net.minecraft.world.item.crafting.SmokingRecipe; -import org.spongepowered.common.item.recipe.ResultFunctionRecipe; -import org.spongepowered.common.item.recipe.ingredient.IngredientResultUtil; -import java.util.Optional; +import java.util.function.Function; -public class SpongeSmokingRecipe extends SmokingRecipe implements ResultFunctionRecipe { +public class SpongeSmokingRecipe extends SmokingRecipe { - private final String resultFunctionId; + private final Function function; - public SpongeSmokingRecipe(final String group, final CookingBookCategory category, final Ingredient ingredient, final ItemStack result, final float experience, final int cookingTime, final String resultFunctionId) { - super(group, category, ingredient, result, experience, cookingTime); - this.resultFunctionId = resultFunctionId; + public SpongeSmokingRecipe( + final String group, + final CookingBookCategory category, + final Ingredient ingredient, + final ItemStackTemplate result, + final float experience, + final int cookingTime, + final Function function + ) { + super(new CommonInfo(false), new CookingBookInfo(category, group), ingredient, result, experience, cookingTime); + this.function = function; } @Override - public Optional resultFunctionId() { - return Optional.ofNullable(this.resultFunctionId); - } - - @Override - public ItemStack assemble(final SingleRecipeInput $$0, final HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - final ItemStack result = IngredientResultUtil.cachedResultFunction(this.resultFunctionId).apply($$0); + public ItemStack assemble(final SingleRecipeInput input) { + if (this.function != null) { + final ItemStack result = this.function.apply(input); result.setCount(1); return result; } - return super.assemble($$0, $$1); - } - - @Override - public ItemStack getResultItem(final HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - return ItemStack.EMPTY; - } - return super.getResultItem($$1); + return super.assemble(input); } diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/locator/PluginsDirectoryLocator.java b/src/main/java/org/spongepowered/common/item/recipe/crafting/RecipeUtil.java similarity index 54% rename from neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/locator/PluginsDirectoryLocator.java rename to src/main/java/org/spongepowered/common/item/recipe/crafting/RecipeUtil.java index b6ad0452b43..180026731e4 100644 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/locator/PluginsDirectoryLocator.java +++ b/src/main/java/org/spongepowered/common/item/recipe/crafting/RecipeUtil.java @@ -22,32 +22,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge.applaunch.loading.moddiscovery.locator; +package org.spongepowered.common.item.recipe.crafting; -import net.neoforged.fml.loading.FMLPaths; -import net.neoforged.fml.loading.moddiscovery.locators.ModsFolderLocator; -import net.neoforged.neoforgespi.ILaunchContext; -import net.neoforged.neoforgespi.locating.IDiscoveryPipeline; -import net.neoforged.neoforgespi.locating.IModFileCandidateLocator; -import org.spongepowered.common.applaunch.AppLaunch; +import net.minecraft.util.context.ContextMap; +import net.minecraft.world.item.crafting.display.DisplayContentsFactory; +import net.minecraft.world.item.crafting.display.SlotDisplayContext; +import org.spongepowered.api.Sponge; +import org.spongepowered.common.SpongeCommon; -import java.nio.file.Path; import java.util.List; -public final class PluginsDirectoryLocator implements IModFileCandidateLocator { +public final class RecipeUtil { - @Override - public void findCandidates(final ILaunchContext context, final IDiscoveryPipeline pipeline) { - final List pluginDirectories = AppLaunch.pluginPlatform().pluginDirectories(); - for (final Path pluginDirectory : pluginDirectories) { - if (pluginDirectory != FMLPaths.MODSDIR.get()) { - new ModsFolderLocator(pluginDirectory, "plugins directory").findCandidates(context, pipeline); - } + public static ContextMap serverBasedContextMap() { + if (!Sponge.isServerAvailable()) { + return new ContextMap.Builder().create(SlotDisplayContext.CONTEXT); } + final var server = SpongeCommon.server(); + return new ContextMap.Builder() + .withParameter(SlotDisplayContext.FUEL_VALUES, server.fuelValues()) + .withParameter(SlotDisplayContext.REGISTRIES, server.registryAccess()) + .create(SlotDisplayContext.CONTEXT); } - @Override - public String toString() { - return "plugins directory"; + public class RemainderResolver implements DisplayContentsFactory.ForRemainders { + @Override + public T addRemainder(T var1, List var2) { + return null; + } } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/crafting/custom/SpongeSpecialCraftingRecipeBuilder.java b/src/main/java/org/spongepowered/common/item/recipe/crafting/custom/SpongeSpecialCraftingRecipeBuilder.java index 358ecafd86e..fe77f6045b0 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/crafting/custom/SpongeSpecialCraftingRecipeBuilder.java +++ b/src/main/java/org/spongepowered/common/item/recipe/crafting/custom/SpongeSpecialCraftingRecipeBuilder.java @@ -25,29 +25,22 @@ package org.spongepowered.common.item.recipe.crafting.custom; import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.crafting.CraftingBookCategory; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.item.inventory.ItemStackLike; -import org.spongepowered.api.item.recipe.RecipeRegistration; import org.spongepowered.api.item.recipe.crafting.RecipeInput; import org.spongepowered.api.item.recipe.crafting.SpecialCraftingRecipe; import org.spongepowered.api.world.server.ServerWorld; -import org.spongepowered.common.util.AbstractResourceKeyedBuilder; import java.util.List; import java.util.function.BiPredicate; import java.util.function.Function; -public final class SpongeSpecialCraftingRecipeBuilder extends AbstractResourceKeyedBuilder - implements SpecialCraftingRecipe.Builder, SpecialCraftingRecipe.Builder.ResultStep, SpecialCraftingRecipe.Builder.EndStep { +public final class SpongeSpecialCraftingRecipeBuilder implements SpecialCraftingRecipe.Builder, SpecialCraftingRecipe.Builder.ResultStep, SpecialCraftingRecipe.Builder.EndStep { private BiPredicate biPredicate; private Function> remainingItemsFunction; private Function resultFunction; - private DataPack pack = DataPacks.RECIPE; private RecipeCategory recipeCategory = RecipeCategory.MISC; // TODO support category @@ -77,25 +70,15 @@ public EndStep result(ItemStackLike result) { } @Override - public EndStep pack(final DataPack pack) { - this.pack = pack; - return this; - } - - @Override - public RecipeRegistration build0() { - final ResourceLocation resourceLocation = (ResourceLocation) (Object) this.key; - // TODO: support categories - return new SpongeSpecialCraftingRecipeRegistration(resourceLocation, CraftingBookCategory.MISC, this.biPredicate, this.remainingItemsFunction, this.resultFunction, this.pack, this.recipeCategory); + public SpecialCraftingRecipe build() { + return (SpecialCraftingRecipe) (Object) new SpongeSpecialRecipe(CraftingBookCategory.MISC, this.biPredicate, this.remainingItemsFunction, this.resultFunction); } @Override public SpecialCraftingRecipe.Builder reset() { - super.reset(); this.biPredicate = null; this.remainingItemsFunction = null; this.resultFunction = null; - this.pack = DataPacks.RECIPE; return this; } diff --git a/src/main/java/org/spongepowered/common/item/recipe/crafting/custom/SpongeSpecialCraftingRecipeRegistration.java b/src/main/java/org/spongepowered/common/item/recipe/crafting/custom/SpongeSpecialCraftingRecipeRegistration.java deleted file mode 100644 index 27a73748ed3..00000000000 --- a/src/main/java/org/spongepowered/common/item/recipe/crafting/custom/SpongeSpecialCraftingRecipeRegistration.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.item.recipe.crafting.custom; - -import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.crafting.CraftingBookCategory; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.recipe.Recipe; -import org.spongepowered.api.item.recipe.RecipeRegistration; -import org.spongepowered.api.item.recipe.crafting.RecipeInput; -import org.spongepowered.api.item.recipe.crafting.SpecialCraftingRecipe; -import org.spongepowered.api.world.server.ServerWorld; -import org.spongepowered.common.item.recipe.SpongeRecipeRegistration; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.BiPredicate; -import java.util.function.Function; - -public class SpongeSpecialCraftingRecipeRegistration extends SpongeRecipeRegistration { - - public static final Map RECIPES = new HashMap<>(); - - private final BiPredicate biPredicate; - private final Function> remainingItemsFunction; - private final Function resultFunction; - - private final SpongeSpecialRecipe recipe; - - public SpongeSpecialCraftingRecipeRegistration(ResourceLocation key, - final CraftingBookCategory category, - BiPredicate biPredicate, - Function> remainingItemsFunction, - Function resultFunction, - DataPack pack, final RecipeCategory recipeCategory) { - super(key, "", pack, recipeCategory, null); - - this.biPredicate = biPredicate; - this.remainingItemsFunction = remainingItemsFunction; - this.resultFunction = resultFunction; - - this.recipe = new SpongeSpecialRecipe(key, category, this.biPredicate, this.remainingItemsFunction, this.resultFunction); - SpongeSpecialCraftingRecipeRegistration.RECIPES.put(this.recipe.id(), this.recipe); - } - - public static SpongeSpecialRecipe get(final String id, final CraftingBookCategory category) { - return SpongeSpecialCraftingRecipeRegistration.RECIPES.getOrDefault(id, - new SpongeSpecialRecipe(ResourceLocation.tryParse(id), category, (x, y) -> false, null, null)); - } - - @Override - public Recipe recipe() { - this.ensureCached(); - return (SpecialCraftingRecipe) (Object) this.recipe; - } -} diff --git a/src/main/java/org/spongepowered/common/item/recipe/crafting/custom/SpongeSpecialRecipe.java b/src/main/java/org/spongepowered/common/item/recipe/crafting/custom/SpongeSpecialRecipe.java index 09531ed46b4..9546e24df0f 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/crafting/custom/SpongeSpecialRecipe.java +++ b/src/main/java/org/spongepowered/common/item/recipe/crafting/custom/SpongeSpecialRecipe.java @@ -24,26 +24,18 @@ */ package org.spongepowered.common.item.recipe.crafting.custom; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.HolderLookup; import net.minecraft.core.NonNullList; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.BannerDuplicateRecipe; import net.minecraft.world.item.crafting.CraftingBookCategory; import net.minecraft.world.item.crafting.CraftingInput; import net.minecraft.world.item.crafting.CustomRecipe; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.level.Level; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; import org.spongepowered.api.item.recipe.crafting.RecipeInput; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.common.inventory.util.InventoryUtil; import org.spongepowered.common.item.util.ItemStackUtil; -import org.spongepowered.common.util.Constants; -import org.spongepowered.common.util.MissingImplementationException; import java.util.List; import java.util.function.BiPredicate; @@ -51,52 +43,37 @@ public final class SpongeSpecialRecipe extends CustomRecipe { - public static final MapCodec SPONGE_CODEC = RecordCodecBuilder.mapCodec( - $$0 -> $$0.group( - Codec.STRING.fieldOf(Constants.Recipe.SPONGE_TYPE).forGetter(SpongeSpecialRecipe::id), // important to fail early when decoding vanilla recipes - CraftingBookCategory.CODEC.fieldOf(Constants.Recipe.CATEGORY).orElse(CraftingBookCategory.MISC).forGetter(SpongeSpecialRecipe::category) - ) - .apply($$0, SpongeSpecialCraftingRecipeRegistration::get) - ); - - private final String id; private final BiPredicate biPredicate; private final Function> remainingItemsFunction; private final Function resultFunction; + private final CraftingBookCategory category; - public SpongeSpecialRecipe(ResourceLocation key, - CraftingBookCategory category, + public SpongeSpecialRecipe(CraftingBookCategory category, BiPredicate biPredicate, Function> remainingItemsFunction, Function resultFunction) { - super(category); - this.id = key.toString(); + super(); + this.category = category; this.biPredicate = biPredicate; this.remainingItemsFunction = remainingItemsFunction; this.resultFunction = resultFunction; } - public String id() { - return id; + @Override + public CraftingBookCategory category() { + return this.category; } - @Override public boolean matches(final CraftingInput input, final Level level) { return this.biPredicate.test(InventoryUtil.toSponge(input), (ServerWorld) level); } @Override - public ItemStack assemble(final CraftingInput input, final HolderLookup.Provider lookup) { + public ItemStack assemble(final CraftingInput input) { return ItemStackUtil.toNative(this.resultFunction.apply(InventoryUtil.toSponge(input))); } - @OnlyIn(Dist.CLIENT) - @Override - public boolean canCraftInDimensions(int width, int height) { - throw new MissingImplementationException("SpongeSpecialRecipe", "canFit"); - } - @Override public NonNullList getRemainingItems(final CraftingInput input) { if (this.remainingItemsFunction == null) { @@ -109,10 +86,10 @@ public NonNullList getRemainingItems(final CraftingInput input) { } @Override - public RecipeSerializer getSerializer() { + public RecipeSerializer getSerializer() { // Fake special crafting serializer // because of Unknown recipe serializer when using our serializer with a vanilla client // return Registry.RECIPE_SERIALIZER.getOrDefault(this.id()); - return RecipeSerializer.BANNER_DUPLICATE; + return BannerDuplicateRecipe.SERIALIZER; } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/crafting/shaped/SpongeShapedCraftingRecipeBuilder.java b/src/main/java/org/spongepowered/common/item/recipe/crafting/shaped/SpongeShapedCraftingRecipeBuilder.java index 58022355737..983d0482ac4 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/crafting/shaped/SpongeShapedCraftingRecipeBuilder.java +++ b/src/main/java/org/spongepowered/common/item/recipe/crafting/shaped/SpongeShapedCraftingRecipeBuilder.java @@ -29,23 +29,18 @@ import it.unimi.dsi.fastutil.chars.Char2ObjectArrayMap; import net.minecraft.core.NonNullList; import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.crafting.CraftingBookCategory; import net.minecraft.world.item.crafting.CraftingInput; import net.minecraft.world.item.crafting.ShapedRecipePattern; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.item.inventory.ItemStackLike; -import org.spongepowered.api.item.recipe.RecipeRegistration; import org.spongepowered.api.item.recipe.crafting.Ingredient; import org.spongepowered.api.item.recipe.crafting.RecipeInput; import org.spongepowered.api.item.recipe.crafting.ShapedCraftingRecipe; import org.spongepowered.common.inventory.util.InventoryUtil; import org.spongepowered.common.item.recipe.ingredient.IngredientUtil; import org.spongepowered.common.item.util.ItemStackUtil; -import org.spongepowered.common.util.AbstractResourceKeyedBuilder; import org.spongepowered.common.util.Preconditions; import java.util.ArrayList; @@ -58,7 +53,7 @@ import java.util.function.Function; import java.util.stream.Collectors; -public final class SpongeShapedCraftingRecipeBuilder extends AbstractResourceKeyedBuilder implements +public final class SpongeShapedCraftingRecipeBuilder implements ShapedCraftingRecipe.Builder, ShapedCraftingRecipe.Builder.AisleStep.ResultStep, ShapedCraftingRecipe.Builder.RowsStep.ResultStep, ShapedCraftingRecipe.Builder.EndStep { @@ -71,7 +66,6 @@ public final class SpongeShapedCraftingRecipeBuilder extends AbstractResourceKey private Function resultFunction; private String group; - private DataPack pack = DataPacks.RECIPE; private RecipeCategory recipeCategory = RecipeCategory.MISC; // TODO support category private CraftingBookCategory craftingBookCategory = CraftingBookCategory.MISC; // TODO support category @@ -169,13 +163,7 @@ public EndStep group(final @Nullable String name) { } @Override - public EndStep pack(final DataPack pack) { - this.pack = pack; - return this; - } - - @Override - public RecipeRegistration build0() { + public ShapedCraftingRecipe build() { Preconditions.checkState(!this.aisle.isEmpty(), "aisle has not been set"); Preconditions.checkState(!this.ingredientMap.isEmpty(), "no ingredients set"); Preconditions.checkState(!this.result.isEmpty(), "no result set"); @@ -197,20 +185,18 @@ public RecipeRegistration build0() { // Default space to Empty Ingredient // ingredientsMap.putIfAbsent(' ', net.minecraft.item.crafting.Ingredient.EMPTY); final ShapedRecipePattern pattern = ShapedRecipePattern.of(ingredientsMap, this.aisle); - return new SpongeShapedCraftingRecipeRegistration((ResourceLocation) (Object) key, this.group, pattern, - ItemStackUtil.toNative(this.result), this.resultFunction, this.remainingItemsFunction, this.pack, this.recipeCategory, this.craftingBookCategory); + return (ShapedCraftingRecipe) new SpongeShapedRecipe(this.group == null ? "": this.group, this.craftingBookCategory, pattern, true, + ItemStackUtil.toTemplate(this.result), this.resultFunction, this.remainingItemsFunction); } @Override public ShapedCraftingRecipe.Builder reset() { - super.reset(); this.aisle = new ArrayList<>(); this.ingredientMap.clear(); this.result = ItemStack.empty(); this.resultFunction = null; this.group = null; this.remainingItemsFunction = null; - this.pack = DataPacks.RECIPE; return this; } diff --git a/src/main/java/org/spongepowered/common/item/recipe/crafting/shaped/SpongeShapedCraftingRecipeRegistration.java b/src/main/java/org/spongepowered/common/item/recipe/crafting/shaped/SpongeShapedCraftingRecipeRegistration.java deleted file mode 100644 index b65c5c09b99..00000000000 --- a/src/main/java/org/spongepowered/common/item/recipe/crafting/shaped/SpongeShapedCraftingRecipeRegistration.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.item.recipe.crafting.shaped; - -import net.minecraft.core.NonNullList; -import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.CraftingBookCategory; -import net.minecraft.world.item.crafting.CraftingInput; -import net.minecraft.world.item.crafting.RecipeSerializer; -import net.minecraft.world.item.crafting.ShapedRecipe; -import net.minecraft.world.item.crafting.ShapedRecipePattern; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.item.recipe.Recipe; -import org.spongepowered.api.item.recipe.RecipeRegistration; -import org.spongepowered.api.item.recipe.crafting.ShapedCraftingRecipe; -import org.spongepowered.common.item.recipe.SpongeRecipeRegistration; - -import java.util.function.Function; - -public class SpongeShapedCraftingRecipeRegistration extends SpongeRecipeRegistration implements - SpongeRecipeRegistration.ResultFunctionRegistration, - SpongeRecipeRegistration.RemainingItemsFunctionRegistration { - - // Vanilla Recipe - private final ShapedRecipePattern pattern; - - // Sponge Recipe - private final ItemStack spongeResult; - private final Function resultFunction; - private final Function> remainingItemsFunction; - private final CraftingBookCategory craftingBookCategory; - private final boolean showNotification = true; - - public SpongeShapedCraftingRecipeRegistration(final ResourceLocation key, final String group, final ShapedRecipePattern pattern, - final ItemStack spongeResult, final Function resultFunction, - final Function> remainingItemsFunction, - final DataPack pack, final RecipeCategory category, final CraftingBookCategory craftingBookCategory) { - super(key, group, pack, category, RecipeSerializer.SHAPED_RECIPE); - this.pattern = pattern; - this.spongeResult = spongeResult; - this.resultFunction = resultFunction; - this.remainingItemsFunction = remainingItemsFunction; - this.craftingBookCategory = craftingBookCategory; - } - - @Override - public Function resultFunction() { - return this.resultFunction; - } - - @Override - public Function> remainingItems() { - return this.remainingItemsFunction; - } - - @Override - public Recipe recipe() { - if (SpongeRecipeRegistration.isVanillaSerializer(this.spongeResult, this.resultFunction, this.remainingItemsFunction, this.pattern.ingredients())) { - return (ShapedCraftingRecipe) new ShapedRecipe(this.group, this.craftingBookCategory, this.pattern, this.spongeResult, this.showNotification); - } - this.ensureCached(); - return (ShapedCraftingRecipe) new SpongeShapedRecipe(this.group, this.craftingBookCategory, this.pattern, this.showNotification, - this.spongeResult, - this.resultFunction == null ? null : this.key.toString(), - this.remainingItemsFunction == null ? null : this.key.toString()); - } - -} diff --git a/src/main/java/org/spongepowered/common/item/recipe/crafting/shaped/SpongeShapedRecipe.java b/src/main/java/org/spongepowered/common/item/recipe/crafting/shaped/SpongeShapedRecipe.java index 0dd447c0c7c..80fc2a55a40 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/crafting/shaped/SpongeShapedRecipe.java +++ b/src/main/java/org/spongepowered/common/item/recipe/crafting/shaped/SpongeShapedRecipe.java @@ -25,105 +25,49 @@ package org.spongepowered.common.item.recipe.crafting.shaped; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.HolderLookup; import net.minecraft.core.NonNullList; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.CraftingBookCategory; import net.minecraft.world.item.crafting.CraftingInput; import net.minecraft.world.item.crafting.ShapedRecipe; import net.minecraft.world.item.crafting.ShapedRecipePattern; -import org.spongepowered.common.bridge.world.item.crafting.RecipeResultBridge; -import org.spongepowered.common.bridge.world.item.crafting.ShapedRecipeBridge; -import org.spongepowered.common.item.recipe.ResultFunctionRecipe; -import org.spongepowered.common.item.recipe.ingredient.IngredientResultUtil; -import org.spongepowered.common.util.Constants; -import java.util.Optional; +import java.util.function.Function; -public class SpongeShapedRecipe extends ShapedRecipe implements ResultFunctionRecipe { +public class SpongeShapedRecipe extends ShapedRecipe { - public static final MapCodec SPONGE_CODEC = RecordCodecBuilder.mapCodec( - $$0 -> $$0.group( - Codec.STRING.fieldOf(Constants.Recipe.SPONGE_TYPE).forGetter(t -> "custom"), // important to fail early when decoding vanilla recipes - Codec.STRING.optionalFieldOf(Constants.Recipe.GROUP, "").forGetter(ShapedRecipe::getGroup), - CraftingBookCategory.CODEC.fieldOf(Constants.Recipe.CATEGORY).orElse(CraftingBookCategory.MISC).forGetter(ShapedRecipe::category), - ShapedRecipePattern.MAP_CODEC.forGetter($$0x -> ((ShapedRecipeBridge) $$0x).bridge$pattern()), - ItemStack.CODEC.fieldOf(Constants.Recipe.RESULT).forGetter($$0x -> ((RecipeResultBridge)$$0x).bridge$result()), - Codec.BOOL.optionalFieldOf("show_notification", true).forGetter(ShapedRecipe::showNotification), - IngredientResultUtil.CACHED_RESULT_FUNC_CODEC.optionalFieldOf(Constants.Recipe.SPONGE_RESULTFUNCTION).forGetter(ResultFunctionRecipe::resultFunctionId), - IngredientResultUtil.CACHED_REMAINING_FUNC_CODEC.optionalFieldOf(Constants.Recipe.SPONGE_REMAINING_ITEMS).forGetter(SpongeShapedRecipe::remainingItemsFunctionId) - ) - .apply($$0, SpongeShapedRecipe::of) - ); - - private final String resultFunctionId; - private final String remainingItemsFunctionId; - - public static SpongeShapedRecipe of( - final String spongeType, - final String groupIn, final CraftingBookCategory category, - final ShapedRecipePattern pattern, - final ItemStack recipeOutputIn, - final boolean showNotification, - final Optional resultFunctionId, - final Optional remainingItemsFunctionId) { - return new SpongeShapedRecipe( - groupIn, - category, - pattern, - showNotification, - recipeOutputIn, - resultFunctionId.orElse(null), - remainingItemsFunctionId.orElse(null)); - } + private final Function resultFunction; + private final Function> remainingItemsFunction; public SpongeShapedRecipe( - final String groupIn, - final CraftingBookCategory category, - final ShapedRecipePattern pattern, - final boolean showNotification, - final ItemStack resultStack, - final String resultFunctionId, - final String remainingItemsFunctionId) { - super(groupIn, category, pattern, resultStack, showNotification); - this.resultFunctionId = resultFunctionId; - this.remainingItemsFunctionId = remainingItemsFunctionId; - } - - @Override - public Optional resultFunctionId() { - return Optional.ofNullable(resultFunctionId); - } - - public Optional remainingItemsFunctionId() { - return Optional.ofNullable(remainingItemsFunctionId); + final String groupIn, + final CraftingBookCategory category, + final ShapedRecipePattern pattern, + final boolean showNotification, + final ItemStackTemplate resultStack, + final Function resultFunction, + final Function> remainingItemsFunction + ) { + super(new CommonInfo(showNotification), new CraftingBookInfo(category, groupIn), pattern, resultStack); + this.resultFunction = resultFunction; + this.remainingItemsFunction = remainingItemsFunction; } @Override public NonNullList getRemainingItems(final CraftingInput $$0) { - if (this.remainingItemsFunctionId != null) { - return IngredientResultUtil.cachedRemainingItemsFunction(this.remainingItemsFunctionId).apply($$0); + if (this.remainingItemsFunction != null) { + return this.remainingItemsFunction.apply($$0); } return super.getRemainingItems($$0); } @Override - public ItemStack assemble(final CraftingInput $$0, final HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - return IngredientResultUtil.cachedResultFunction(this.resultFunctionId).apply($$0); - } - return super.assemble($$0, $$1); - } - - @Override - public ItemStack getResultItem(final HolderLookup.Provider $$0) { - if (this.resultFunctionId != null) { - return ItemStack.EMPTY; + public ItemStack assemble(final CraftingInput input) { + if (this.resultFunction != null) { + return this.resultFunction.apply(input); } - return super.getResultItem($$0); + return super.assemble(input); } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessCraftingRecipeBuilder.java b/src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessCraftingRecipeBuilder.java index f048d2256e0..5fa09531b17 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessCraftingRecipeBuilder.java +++ b/src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessCraftingRecipeBuilder.java @@ -27,24 +27,19 @@ import net.minecraft.core.NonNullList; import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.CraftingBookCategory; import net.minecraft.world.item.crafting.CraftingInput; import net.minecraft.world.item.crafting.Ingredient; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.item.ItemType; import org.spongepowered.api.item.inventory.ItemStackLike; -import org.spongepowered.api.item.recipe.RecipeRegistration; import org.spongepowered.api.item.recipe.crafting.RecipeInput; import org.spongepowered.api.item.recipe.crafting.ShapelessCraftingRecipe; import org.spongepowered.common.inventory.util.InventoryUtil; import org.spongepowered.common.item.recipe.ingredient.IngredientUtil; import org.spongepowered.common.item.util.ItemStackUtil; -import org.spongepowered.common.util.AbstractResourceKeyedBuilder; import org.spongepowered.common.util.Preconditions; import java.util.List; @@ -52,15 +47,13 @@ import java.util.function.Function; import java.util.function.Supplier; -public class SpongeShapelessCraftingRecipeBuilder extends AbstractResourceKeyedBuilder - implements ShapelessCraftingRecipe.Builder.EndStep, ShapelessCraftingRecipe.Builder.ResultStep { +public class SpongeShapelessCraftingRecipeBuilder implements ShapelessCraftingRecipe.Builder.EndStep, ShapelessCraftingRecipe.Builder.ResultStep { private org.spongepowered.api.item.inventory.ItemStack result; private Function resultFunction; private Function> remainingItemsFunction; private final NonNullList ingredients = NonNullList.create(); private String group; - private DataPack pack = DataPacks.RECIPE; private RecipeCategory recipeCategory = RecipeCategory.MISC; // TODO support category private CraftingBookCategory craftingBookCategory = CraftingBookCategory.MISC; // TODO support category @@ -121,28 +114,19 @@ public EndStep group(final @Nullable String name) { } @Override - public EndStep pack(final DataPack pack) { - this.pack = pack; - return this; - } - - @Override - public RecipeRegistration build0() { + public ShapelessCraftingRecipe build() { Preconditions.checkState(!this.ingredients.isEmpty(), "The ingredients are not set."); - return new SpongeShapelessCraftingRecipeRegistration((ResourceLocation) (Object) key, this.group, this.ingredients, - ItemStackUtil.toNative(this.result), this.resultFunction, this.remainingItemsFunction, this.pack, - this.recipeCategory, this.craftingBookCategory); + return (ShapelessCraftingRecipe) new SpongeShapelessRecipe(this.group == null ? "": this.group, this.craftingBookCategory, this.ingredients, + ItemStackUtil.toTemplate(this.result), this.resultFunction, this.remainingItemsFunction); } @Override public ShapelessCraftingRecipe.Builder reset() { - super.reset(); this.result = null; this.resultFunction = null; this.ingredients.clear(); this.group = null; this.remainingItemsFunction = null; - this.pack = DataPacks.RECIPE; return this; } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessCraftingRecipeRegistration.java b/src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessCraftingRecipeRegistration.java deleted file mode 100644 index c70e8658c1f..00000000000 --- a/src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessCraftingRecipeRegistration.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.item.recipe.crafting.shapeless; - -import net.minecraft.core.NonNullList; -import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.CraftingBookCategory; -import net.minecraft.world.item.crafting.CraftingInput; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.RecipeSerializer; -import net.minecraft.world.item.crafting.ShapelessRecipe; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.item.recipe.Recipe; -import org.spongepowered.api.item.recipe.RecipeRegistration; -import org.spongepowered.api.item.recipe.crafting.ShapelessCraftingRecipe; -import org.spongepowered.common.item.recipe.SpongeRecipeRegistration; - -import java.util.List; -import java.util.function.Function; - -public class SpongeShapelessCraftingRecipeRegistration extends SpongeRecipeRegistration implements - SpongeRecipeRegistration.ResultFunctionRegistration, - SpongeRecipeRegistration.RemainingItemsFunctionRegistration -{ - // Vanilla Recipe - private final List ingredients; - - // Sponge Recipe - private final ItemStack spongeResult; - private final Function resultFunction; - private final Function> remainingItemsFunction; - private final CraftingBookCategory craftingBookCategory; - - public SpongeShapelessCraftingRecipeRegistration(final ResourceLocation key, final String group, - final List ingredients, final ItemStack spongeResult, final Function resultFunction, - final Function> remainingItemsFunction, - final DataPack pack, final RecipeCategory category, final CraftingBookCategory craftingBookCategory) { - super(key, group, pack, category, RecipeSerializer.SHAPELESS_RECIPE); - this.ingredients = ingredients; - this.spongeResult = spongeResult; - this.resultFunction = resultFunction; - this.remainingItemsFunction = remainingItemsFunction; - this.craftingBookCategory = craftingBookCategory; - } - - - @Override - public Function resultFunction() { - return this.resultFunction; - } - - @Override - public Function> remainingItems() { - return this.remainingItemsFunction; - } - - @Override - public Recipe recipe() { - this.ensureCached(); - final NonNullList ingredients = NonNullList.create(); - ingredients.addAll(this.ingredients); - - if (SpongeRecipeRegistration.isVanillaSerializer(this.spongeResult, this.resultFunction, this.remainingItemsFunction, ingredients)) { - return (ShapelessCraftingRecipe) new ShapelessRecipe(this.group, this.craftingBookCategory, this.spongeResult, ingredients); - } - - return (ShapelessCraftingRecipe) new SpongeShapelessRecipe(this.group, this.craftingBookCategory, ingredients, this.spongeResult, - this.resultFunction == null ? null : this.key.toString(), - this.remainingItemsFunction == null ? null : this.key.toString()); - - } -} diff --git a/src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessRecipe.java b/src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessRecipe.java index f103a729ca0..d40a96f2782 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessRecipe.java +++ b/src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessRecipe.java @@ -24,22 +24,16 @@ */ package org.spongepowered.common.item.recipe.crafting.shapeless; -import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.HolderLookup; import net.minecraft.core.NonNullList; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.CraftingBookCategory; import net.minecraft.world.item.crafting.CraftingInput; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.ShapelessRecipe; import net.minecraft.world.level.Level; -import org.spongepowered.common.bridge.world.item.crafting.RecipeResultBridge; -import org.spongepowered.common.item.recipe.ingredient.IngredientResultUtil; +import org.spongepowered.common.accessor.world.item.crafting.ShapelessRecipeAccessor; import org.spongepowered.common.item.recipe.ingredient.SpongeIngredient; -import org.spongepowered.common.util.Constants; import java.util.ArrayList; import java.util.Collection; @@ -48,80 +42,31 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; +import java.util.function.Function; /** * Customized matching algorithm matching with ingredient predicate instead of packed item in vanilla */ public class SpongeShapelessRecipe extends ShapelessRecipe { - - public static final MapCodec SPONGE_CODEC = RecordCodecBuilder.mapCodec( - $$0 -> $$0.group( - Codec.STRING.fieldOf(Constants.Recipe.SPONGE_TYPE).forGetter(t -> "custom"), // important to fail early when decoding vanilla recipes - Codec.STRING.optionalFieldOf("group", "").forGetter(ShapelessRecipe::getGroup), - CraftingBookCategory.CODEC.fieldOf("category").orElse(CraftingBookCategory.MISC).forGetter(ShapelessRecipe::category), - ItemStack.CODEC.fieldOf(Constants.Recipe.RESULT).forGetter($$0x -> ((RecipeResultBridge)$$0x).bridge$result()), - Ingredient.CODEC_NONEMPTY - .listOf() - .fieldOf(Constants.Recipe.SHAPELESS_INGREDIENTS) - .flatXmap( - $$0x -> { - Ingredient[] $$1 = $$0x.stream().filter($$0xx -> !$$0xx.isEmpty()).toArray(Ingredient[]::new); - if ($$1.length == 0) { - return DataResult.error(() -> "No ingredients for shapeless recipe"); - } else { - return $$1.length > 9 - ? DataResult.error(() -> "Too many ingredients for shapeless recipe") - : DataResult.success(NonNullList.of(Ingredient.EMPTY, $$1)); - } - }, - DataResult::success - ) - .forGetter(ShapelessRecipe::getIngredients), - IngredientResultUtil.CACHED_RESULT_FUNC_CODEC.optionalFieldOf(Constants.Recipe.SPONGE_RESULTFUNCTION).forGetter(SpongeShapelessRecipe::resultFunctionId), - IngredientResultUtil.CACHED_REMAINING_FUNC_CODEC.optionalFieldOf(Constants.Recipe.SPONGE_REMAINING_ITEMS).forGetter(SpongeShapelessRecipe::remainingItemsFunctionId) - ) - .apply($$0, SpongeShapelessRecipe::of) - ); private final boolean onlyVanillaIngredients; - private final String resultFunctionId; - private final String remainingItemsFunctionId; - - - public static SpongeShapelessRecipe of( - final String spongeType, - final String groupIn, - final CraftingBookCategory category, - final ItemStack recipeOutputIn, - final NonNullList recipeItemsIn, - final Optional resultFunctionId, - final Optional remainingItemsFunctionId) - { - return new SpongeShapelessRecipe(groupIn, category, recipeItemsIn, recipeOutputIn, - resultFunctionId.orElse(null), remainingItemsFunctionId.orElse(null)); - } - - public SpongeShapelessRecipe(final String groupIn, - final CraftingBookCategory category, - final NonNullList recipeItemsIn, - final ItemStack spongeResultStack, - final String resultFunctionId, - final String remainingItemsFunctionId) { - super(groupIn, category, spongeResultStack, recipeItemsIn); + private final Function resultFunction; + private final Function> remainingItemsFunction; + + public SpongeShapelessRecipe( + final String groupIn, + final CraftingBookCategory category, + final List recipeItemsIn, + final ItemStackTemplate spongeResultStack, + final Function resultFunction, + final Function> remainingItemsFunction + ) { + super(new CommonInfo(false), new CraftingBookInfo(category, groupIn), spongeResultStack, recipeItemsIn); this.onlyVanillaIngredients = recipeItemsIn.stream().noneMatch(i -> i instanceof SpongeIngredient); - this.resultFunctionId = resultFunctionId; - this.remainingItemsFunctionId = remainingItemsFunctionId; - } - - public Optional resultFunctionId() { - return Optional.ofNullable(resultFunctionId); - } - - public Optional remainingItemsFunctionId() { - return Optional.ofNullable(remainingItemsFunctionId); + this.resultFunction = resultFunction; + this.remainingItemsFunction = remainingItemsFunction; } @Override @@ -129,32 +74,24 @@ public boolean matches(final CraftingInput $$0, final Level $$1) { if (this.onlyVanillaIngredients) { return super.matches($$0, $$1); } - return SpongeShapelessRecipe.matches($$0, this.getIngredients()); + return SpongeShapelessRecipe.matches($$0, ((ShapelessRecipeAccessor) this).accessor$ingredients()); } @Override public NonNullList getRemainingItems(final CraftingInput $$0) { - if (this.remainingItemsFunctionId != null) { - return IngredientResultUtil.cachedRemainingItemsFunction(this.remainingItemsFunctionId).apply($$0); + if (this.remainingItemsFunction != null) { + return this.remainingItemsFunction.apply($$0); } return super.getRemainingItems($$0); } @Override - public ItemStack assemble(final CraftingInput $$0, final HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - return IngredientResultUtil.cachedResultFunction(this.resultFunctionId).apply($$0); + public ItemStack assemble(final CraftingInput input) { + if (this.resultFunction != null) { + return this.resultFunction.apply(input); } - return super.assemble($$0, $$1); - } - - @Override - public ItemStack getResultItem(final HolderLookup.Provider $$1) { -// if (this.resultFunctionId != null) { -// return ItemStack.EMPTY; -// } - return super.getResultItem($$1); + return super.assemble(input); } private static boolean @@ -174,7 +111,8 @@ public ItemStack getResultItem(final HolderLookup.Provider $$1) { for (int j = 0; j < stacks.size(); j++) { final ItemStack stack = stacks.get(j); if (!stack.isEmpty() && ingredient.test(stack)) { - matchesMap.computeIfAbsent(j, k -> new ArrayList<>()).add(i);; + matchesMap.computeIfAbsent(j, k -> new ArrayList<>()).add(i); + ; noMatch = false; } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/ingredient/IngredientResultUtil.java b/src/main/java/org/spongepowered/common/item/recipe/ingredient/IngredientResultUtil.java deleted file mode 100644 index 11d658bef51..00000000000 --- a/src/main/java/org/spongepowered/common/item/recipe/ingredient/IngredientResultUtil.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.item.recipe.ingredient; - -import com.google.gson.JsonElement; -import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.JsonOps; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.core.NonNullList; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.Container; -import net.minecraft.world.item.crafting.CraftingInput; -import net.minecraft.world.item.crafting.RecipeInput; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.common.SpongeCommon; - -import java.text.MessageFormat; -import java.util.Map; -import java.util.function.Function; - -public final class IngredientResultUtil { - - private static final Map> cachedResultFunctions = new Object2ObjectOpenHashMap<>(); - private static final Map>> cachedRemainingItemsFunctions = new Object2ObjectOpenHashMap<>(); - - public static JsonElement serializeItemStack(final net.minecraft.world.item.ItemStack spongeResult) { - final DataResult encoded = net.minecraft.world.item.ItemStack.CODEC.encodeStart(JsonOps.INSTANCE, spongeResult); - return encoded.result().get(); - - } - public static final Codec CACHED_RESULT_FUNC_CODEC = Codec.STRING.flatXmap( - id -> IngredientResultUtil.cachedResultFunction(id) != null ? DataResult.success(id) : - DataResult.error(() -> "Missing Result Function for id " + id), - DataResult::success - ); - - @Nullable - @SuppressWarnings("unchecked") - public static Function cachedResultFunction(String id) { - return (Function) IngredientResultUtil.cachedResultFunctions.get(id); - } - - - public static String cacheResultFunction(ResourceLocation id, - Function resultFunction) { - if (IngredientResultUtil.cachedResultFunctions.put(id.toString(), resultFunction) != null) { - SpongeCommon.logger().warn(MessageFormat.format( - "Duplicate cache result registration! " + id + " was replaced.", - new Object[]{} - )); - } - return id.toString(); - } - - public static final Codec CACHED_REMAINING_FUNC_CODEC = Codec.STRING.flatXmap( - id -> IngredientResultUtil.cachedRemainingItemsFunction(id) != null ? DataResult.success(id) : - DataResult.error(() -> "Missing Result Function for id " + id), - DataResult::success - ); - - @Nullable - @SuppressWarnings("unchecked") - public static Function> cachedRemainingItemsFunction(String id) { - return (Function>) IngredientResultUtil.cachedRemainingItemsFunctions.get(id); - } - - - public static String cacheRemainingItemsFunction(ResourceLocation id, - Function> resultFunction) { - if (IngredientResultUtil.cachedRemainingItemsFunctions.put(id.toString(), resultFunction) != null) { - SpongeCommon.logger().warn(MessageFormat.format( - "Duplicate cache result registration! " + id + " was replaced.", - new Object[]{} - )); - } - return id.toString(); - } - - public static void clearCache() { - IngredientResultUtil.cachedResultFunctions.clear(); - IngredientResultUtil.cachedRemainingItemsFunctions.clear(); - } -} diff --git a/src/main/java/org/spongepowered/common/item/recipe/ingredient/IngredientUtil.java b/src/main/java/org/spongepowered/common/item/recipe/ingredient/IngredientUtil.java index 23c02c5921f..a1baea3bc60 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/ingredient/IngredientUtil.java +++ b/src/main/java/org/spongepowered/common/item/recipe/ingredient/IngredientUtil.java @@ -24,8 +24,10 @@ */ package org.spongepowered.common.item.recipe.ingredient; +import net.minecraft.core.HolderSet; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.crafting.Ingredient; @@ -58,8 +60,9 @@ public static org.spongepowered.api.item.recipe.crafting.Ingredient of(ItemType. } public static org.spongepowered.api.item.recipe.crafting.@Nullable Ingredient of(ResourceKey tagKey) { - final TagKey key = TagKey.create(Registries.ITEM, (ResourceLocation) (Object) tagKey); - return IngredientUtil.fromNative(Ingredient.of(key)); + final TagKey key = TagKey.create(Registries.ITEM, (Identifier) (Object) tagKey); + var holderset = BuiltInRegistries.ITEM.get(key).map(hs -> (HolderSet) hs).orElse(HolderSet.empty()); + return IngredientUtil.fromNative(Ingredient.of(holderset)); } private static net.minecraft.world.item.ItemStack[] toNativeStacks(ItemStack[] stacks) { diff --git a/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongeIngredient.java b/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongeIngredient.java index b6688459486..c9be75eea61 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongeIngredient.java +++ b/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongeIngredient.java @@ -28,23 +28,24 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.HolderSet; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.display.SlotDisplay; import org.apache.commons.lang3.NotImplementedException; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.item.inventory.ItemStackLike; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.item.util.ItemStackUtil; -import java.text.MessageFormat; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Predicate; -import java.util.stream.Stream; public class SpongeIngredient extends Ingredient { @@ -83,8 +84,8 @@ public class SpongeIngredient extends Ingredient { if (predicate == null) { return DataResult.error(() -> "Could not find predicate for custom ingredient with id " + raw.predicateId.get()); } - final SpongeIngredient ingredient = new SpongeIngredient(raw.type, - new SpongePredicateItemList(raw.predicateId.get(), predicate, raw.stacks.toArray(new ItemStack[0])), raw.predicateId.orElse(null)); + final var predicateList = new SpongePredicateItemList(raw.predicateId.get(), predicate, raw.stacks.toArray(new ItemStack[0])); + final SpongeIngredient ingredient = new SpongeIngredient(raw.type, predicateList, raw.predicateId.orElse(null)); return DataResult.success(ingredient); } default -> { @@ -94,11 +95,11 @@ public class SpongeIngredient extends Ingredient { }, spongeIngredient -> { switch (spongeIngredient.type) { case SpongeStackItemList.TYPE_STACK -> { - var stacks = Arrays.stream(spongeIngredient.values).flatMap(v -> v.getItems().stream()).toList(); + final var stacks = Arrays.asList(spongeIngredient.itemList.stacks); return DataResult.success(new SpongeRawIngredient(spongeIngredient.type, stacks, Optional.empty())); } case SpongePredicateItemList.TYPE_PREDICATE -> { - var stacks = Arrays.stream(spongeIngredient.values).flatMap(v -> v.getItems().stream()).toList(); + final var stacks = Arrays.asList(spongeIngredient.itemList.stacks); return DataResult.success(new SpongeRawIngredient(spongeIngredient.type, stacks, Optional.ofNullable(spongeIngredient.predicateId))); } } @@ -106,19 +107,17 @@ public class SpongeIngredient extends Ingredient { }); public final String type; public final String predicateId; + private final SpongeItemList itemList; record SpongeRawIngredient(String type, List stacks, Optional predicateId) { } - public SpongeIngredient(final String type, final Stream values, final String predicateId) { - super(values); + public SpongeIngredient(final String type, final SpongeItemList itemList, final String predicateId) { + super(HolderSet.direct(itemList.stream().toList())); this.type = type; this.predicateId = predicateId; - } - - public SpongeIngredient(final String type, final Ingredient.Value value, final String predicateId) { - this(type, Stream.of(value), predicateId); + this.itemList = itemList; } public static void clearCache() { @@ -131,22 +130,16 @@ public boolean test(final ItemStack testStack) { return false; } - for (final Value acceptedItem : this.values) { - if (acceptedItem instanceof SpongeItemList) { - if (((SpongeItemList) acceptedItem).test(testStack)) { - return true; - } - } else { - // TODO caching (relevant for TagList) - for (final ItemStack stack : acceptedItem.getItems()) { - if (stack.getItem() == testStack.getItem()) { - return true; - } - } - } - } + return this.itemList.test(testStack); + } - return false; + @Override + public SlotDisplay display() { + return new SlotDisplay.Composite(this.itemList.getItems().stream() + .filter(ItemStack::isEmpty) + .map(ItemStackTemplate::fromNonEmptyStack) + .map(SlotDisplay.ItemStackSlotDisplay::new) + .map(SlotDisplay.class::cast).toList()); } public static SpongeIngredient spongeFromStacks(net.minecraft.world.item.ItemStack... stacks) { @@ -163,7 +156,7 @@ public static SpongeIngredient spongeFromPredicate(ResourceKey key, Predicate { public static final String INGREDIENT_TYPE = "sponge:type"; public static final String INGREDIENT_ITEM = "sponge:item"; @@ -44,18 +54,64 @@ public SpongeItemList(ItemStack... stacks) { } @Override - public Collection getItems() { - return Arrays.asList(this.stacks); + public Stream> stream() { + return Arrays.stream(this.stacks) + .map(ItemStack::typeHolder); + } + + @Override + public int size() { + return this.stacks.length; + } + + @Override + public boolean isBound() { + return true; } - public JsonObject serialize() { - final JsonObject jsonobject = new JsonObject(); - final JsonArray stackArray = new JsonArray(); - for (ItemStack stack : this.stacks) { - stackArray.add(IngredientResultUtil.serializeItemStack(stack)); - } - jsonobject.add(SpongeItemList.INGREDIENT_ITEM, stackArray); - return jsonobject; + @Override + public Either, List>> unwrap() { + return Either.right(this.stream().toList()); + } + + @Override + public Optional> getRandomElement(RandomSource source) { + return this.stacks.length == 0 ? Optional.empty() : + Optional.of(Util.getRandom(this.stacks, source)). + map(ItemStack::typeHolder); + } + + @Override + public Holder get(int var1) { + final var stack = this.stacks[var1]; + return stack.typeHolder(); + } + + @Override + public boolean contains(Holder var1) { + return false; + } + + @Override + public boolean canSerializeIn(HolderOwner var1) { + // TODO - maybe? What's this for? + return true; + } + + @Override + public Optional> unwrapKey() { + return Optional.empty(); + } + + @Override + public @NotNull Iterator> iterator() { + return Arrays.stream(this.stacks) + .map(ItemStack::typeHolder) + .iterator(); + } + + public Collection getItems() { + return Arrays.asList(this.stacks); } public abstract boolean test(ItemStack stack); diff --git a/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongePredicateItemList.java b/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongePredicateItemList.java index 8c7a12fca86..f0d83b03ac4 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongePredicateItemList.java +++ b/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongePredicateItemList.java @@ -24,7 +24,6 @@ */ package org.spongepowered.common.item.recipe.ingredient; -import com.google.gson.JsonObject; import net.minecraft.world.item.ItemStack; import java.util.function.Predicate; @@ -43,14 +42,6 @@ public SpongePredicateItemList(String id, Predicate predicate, ItemSt this.predicate = predicate; } - @Override - public JsonObject serialize() { - final JsonObject jsonobject = super.serialize(); - jsonobject.addProperty(SpongeItemList.INGREDIENT_TYPE, SpongePredicateItemList.TYPE_PREDICATE); - jsonobject.addProperty(SpongePredicateItemList.INGREDIENT_PREDICATE, this.id); - return jsonobject; - } - @Override public boolean test(ItemStack stack) { return this.predicate.test(stack); diff --git a/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongeStackItemList.java b/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongeStackItemList.java index 92aa5397364..bd230c3b71c 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongeStackItemList.java +++ b/src/main/java/org/spongepowered/common/item/recipe/ingredient/SpongeStackItemList.java @@ -24,7 +24,6 @@ */ package org.spongepowered.common.item.recipe.ingredient; -import com.google.gson.JsonObject; import net.minecraft.world.item.ItemStack; public class SpongeStackItemList extends SpongeItemList { @@ -35,13 +34,6 @@ public SpongeStackItemList(ItemStack... stacks) { super(stacks); } - @Override - public JsonObject serialize() { - final JsonObject jsonobject = super.serialize(); - jsonobject.addProperty(SpongeItemList.INGREDIENT_TYPE, SpongeStackItemList.TYPE_STACK); - return jsonobject; - } - @Override public boolean test(ItemStack testStack) { if (this.stacks.length == 0) { diff --git a/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeArmorTrimFactory.java b/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeArmorTrimFactory.java index 03439ac7176..3b570aca117 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeArmorTrimFactory.java +++ b/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeArmorTrimFactory.java @@ -39,9 +39,9 @@ public ArmorTrim create(TrimMaterial material, TrimPattern pattern) { final var trimRegistry = Sponge.server().registry(RegistryTypes.TRIM_MATERIAL); final var patternRegistry = Sponge.server().registry(RegistryTypes.TRIM_PATTERN); - final var materialHolder = ((Registry) trimRegistry).wrapAsHolder((net.minecraft.world.item.armortrim.TrimMaterial) (Object) material); - final var patternHolder = ((Registry) patternRegistry).wrapAsHolder((net.minecraft.world.item.armortrim.TrimPattern) (Object) pattern); - return (ArmorTrim) new net.minecraft.world.item.armortrim.ArmorTrim(materialHolder, patternHolder); + final var materialHolder = ((Registry) trimRegistry).wrapAsHolder((net.minecraft.world.item.equipment.trim.TrimMaterial) (Object) material); + final var patternHolder = ((Registry) patternRegistry).wrapAsHolder((net.minecraft.world.item.equipment.trim.TrimPattern) (Object) pattern); + return (ArmorTrim) (Object) new net.minecraft.world.item.equipment.trim.ArmorTrim(materialHolder, patternHolder); } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeSmithingRecipe.java b/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeSmithingRecipe.java index bff50643488..712c8b8ceb7 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeSmithingRecipe.java +++ b/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeSmithingRecipe.java @@ -24,82 +24,33 @@ */ package org.spongepowered.common.item.recipe.smithing; -import static org.spongepowered.common.util.Constants.Recipe.SPONGE_TYPE; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.HolderLookup; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.SmithingRecipeInput; import net.minecraft.world.item.crafting.SmithingTransformRecipe; -import org.spongepowered.common.bridge.world.item.crafting.RecipeResultBridge; -import org.spongepowered.common.bridge.world.item.crafting.SmithingRecipeBridge; -import org.spongepowered.common.item.recipe.ResultFunctionRecipe; -import org.spongepowered.common.item.recipe.ingredient.IngredientResultUtil; -import org.spongepowered.common.util.Constants; import java.util.Optional; +import java.util.function.Function; -public class SpongeSmithingRecipe extends SmithingTransformRecipe implements ResultFunctionRecipe { +public class SpongeSmithingRecipe extends SmithingTransformRecipe { - public static final MapCodec SPONGE_CODEC = RecordCodecBuilder.mapCodec( - $$0 -> $$0.group( - Codec.STRING.fieldOf(SPONGE_TYPE).forGetter(a -> "custom"), - Ingredient.CODEC.fieldOf("template").forGetter($$0x -> ((SmithingRecipeBridge) $$0x).bridge$template()), - Ingredient.CODEC.fieldOf(Constants.Recipe.SMITHING_BASE_INGREDIENT).forGetter($$0x -> ((SmithingRecipeBridge) $$0x).bridge$base()), - Ingredient.CODEC.fieldOf(Constants.Recipe.SMITHING_ADDITION_INGREDIENT).forGetter($$0x -> ((SmithingRecipeBridge) $$0x).bridge$addition()), - ItemStack.CODEC.fieldOf(Constants.Recipe.RESULT).forGetter($$0x -> ((RecipeResultBridge) $$0x).bridge$result()), - IngredientResultUtil.CACHED_RESULT_FUNC_CODEC.optionalFieldOf(Constants.Recipe.SPONGE_RESULTFUNCTION).forGetter(ResultFunctionRecipe::resultFunctionId) - ) - .apply($$0, SpongeSmithingRecipe::of) - ); + private Function resultFunction; - private final String resultFunctionId; - - public static SpongeSmithingRecipe of(final String spongeType, final Ingredient template, final Ingredient base, - final Ingredient addition, final ItemStack resultIn, final Optional resultFunctionId) - { - return new SpongeSmithingRecipe(template, base, addition, resultIn, resultFunctionId.orElse(null)); - } - - public SpongeSmithingRecipe(final Ingredient template, final Ingredient base, - final Ingredient addition, final ItemStack spongeResult, final String resultFunctionId) { - super(template, base, addition, spongeResult); - this.resultFunctionId = resultFunctionId; - } - - @Override - public Optional resultFunctionId() { - return Optional.ofNullable(this.resultFunctionId); + public SpongeSmithingRecipe(final Optional template, final Ingredient base, + final Optional addition, final ItemStackTemplate spongeResult, final Function resultFunction) { + super(new CommonInfo(false), template, base, addition, spongeResult); + this.resultFunction = resultFunction; } @Override - public ItemStack assemble(final SmithingRecipeInput $$0, final HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - return IngredientResultUtil.cachedResultFunction(this.resultFunctionId).apply($$0); + public ItemStack assemble(final SmithingRecipeInput input) { + if (this.resultFunction != null) { + return this.resultFunction.apply(input); } - final ItemStack resultItem = this.getResultItem($$1); - if (!resultItem.getComponents().isEmpty()) { - final ItemStack itemStack = resultItem.copy(); - var patch = $$0.getItem(0).getComponentsPatch(); - if (!patch.isEmpty()) { - itemStack.applyComponents(patch); - return itemStack; - } - } - return super.assemble($$0, $$1); - } - - @Override - public ItemStack getResultItem(HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - return ItemStack.EMPTY; - } - return super.getResultItem($$1); + return super.assemble(input); } - } diff --git a/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeSmithingRecipeBuilder.java b/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeSmithingRecipeBuilder.java index d7eeb1bd5e7..4e1c8bbc666 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeSmithingRecipeBuilder.java +++ b/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeSmithingRecipeBuilder.java @@ -26,38 +26,37 @@ import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.SmithingRecipeInput; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; +import org.checkerframework.framework.qual.DefaultQualifier; import org.spongepowered.api.item.ItemType; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.item.inventory.ItemStackLike; -import org.spongepowered.api.item.recipe.RecipeRegistration; import org.spongepowered.api.item.recipe.crafting.RecipeInput; import org.spongepowered.api.item.recipe.smithing.SmithingRecipe; import org.spongepowered.common.inventory.util.InventoryUtil; import org.spongepowered.common.item.recipe.ingredient.IngredientUtil; import org.spongepowered.common.item.util.ItemStackUtil; -import org.spongepowered.common.util.AbstractResourceKeyedBuilder; import org.spongepowered.common.util.Preconditions; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; -public final class SpongeSmithingRecipeBuilder extends AbstractResourceKeyedBuilder implements +@DefaultQualifier(NonNull.class) +public final class SpongeSmithingRecipeBuilder implements SmithingRecipe.Builder, SmithingRecipe.Builder.BaseStep, SmithingRecipe.Builder.AdditionStep, SmithingRecipe.Builder.ResultStep, SmithingRecipe.Builder.EndStep { - private ItemStack result; - private Ingredient template; - private Ingredient base; - private Ingredient addition; - private Function resultFunction; + private @MonotonicNonNull ItemStack result; + private @MonotonicNonNull Ingredient template; + private @MonotonicNonNull Ingredient base; + private @MonotonicNonNull Ingredient addition; + private @MonotonicNonNull Function resultFunction; private @Nullable String group; - private DataPack pack = DataPacks.RECIPE; private RecipeCategory recipeCategory = RecipeCategory.MISC; // TODO support category @@ -122,15 +121,9 @@ public EndStep group(@Nullable String name) { } @Override - public EndStep pack(final DataPack pack) { - this.pack = pack; - return this; - } - - @Override - public RecipeRegistration build0() { - return new SpongeSmithingRecipeRegistration((ResourceLocation) (Object) key, this.group, this.template, this.base, this.addition, - ItemStackUtil.toNative(this.result), this.resultFunction, this.pack, this.recipeCategory); + public SmithingRecipe build() { + return (SmithingRecipe) new SpongeSmithingRecipe(Optional.ofNullable(this.template), this.base, Optional.ofNullable(this.addition), + ItemStackUtil.toTemplate(this.result), this.resultFunction); } @Override @@ -140,7 +133,6 @@ public SmithingRecipe.Builder reset() { this.base = null; this.addition = null; this.group = null; - this.pack = DataPacks.RECIPE; - return super.reset(); + return this; } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeSmithingRecipeRegistration.java b/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeSmithingRecipeRegistration.java deleted file mode 100644 index 39a62cbb37a..00000000000 --- a/src/main/java/org/spongepowered/common/item/recipe/smithing/SpongeSmithingRecipeRegistration.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.item.recipe.smithing; - -import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.RecipeSerializer; -import net.minecraft.world.item.crafting.SmithingRecipeInput; -import net.minecraft.world.item.crafting.SmithingTransformRecipe; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.item.recipe.Recipe; -import org.spongepowered.api.item.recipe.RecipeRegistration; -import org.spongepowered.api.item.recipe.smithing.SmithingRecipe; -import org.spongepowered.common.item.recipe.SpongeRecipeRegistration; - -import java.util.List; -import java.util.function.Function; - -public class SpongeSmithingRecipeRegistration extends SpongeRecipeRegistration implements - SpongeRecipeRegistration.ResultFunctionRegistration { - - // Vanilla Recipe - private final Ingredient template; - - private final Ingredient base; - private final Ingredient addition; - - // Sponge Recipe - private final ItemStack spongeResult; - private final Function resultFunction; - - public SpongeSmithingRecipeRegistration(ResourceLocation key, String group, final Ingredient template, Ingredient base, - Ingredient addition, ItemStack spongeResult, Function resultFunction, - DataPack pack, final RecipeCategory category) { - super(key, group, pack, category, RecipeSerializer.SMITHING_TRANSFORM); - this.template = template; - this.base = base; - this.addition = addition; - this.spongeResult = spongeResult; - this.resultFunction = resultFunction; - } - - @Override - public Recipe recipe() { - this.ensureCached(); - if (SpongeRecipeRegistration.isVanillaSerializer(this.spongeResult, this.resultFunction, null, List.of(this.template, this.base, this.addition))) { - return (SmithingRecipe) new SmithingTransformRecipe(this.template, this.base, this.addition, this.spongeResult); - - } - return (SmithingRecipe) new SpongeSmithingRecipe(this.template, this.base, this.addition, this.spongeResult, this.resultFunction == null ? null : this.key.toString()); - } - - @Override - public Function resultFunction() { - return this.resultFunction; - } -} diff --git a/src/main/java/org/spongepowered/common/item/recipe/stonecutting/SpongeStoneCutterRecipeBuilder.java b/src/main/java/org/spongepowered/common/item/recipe/stonecutting/SpongeStoneCutterRecipeBuilder.java index 0c00ca927f2..ac1c2c1971a 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/stonecutting/SpongeStoneCutterRecipeBuilder.java +++ b/src/main/java/org/spongepowered/common/item/recipe/stonecutting/SpongeStoneCutterRecipeBuilder.java @@ -26,36 +26,30 @@ import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.SingleRecipeInput; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.item.ItemType; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.item.inventory.ItemStackLike; -import org.spongepowered.api.item.recipe.RecipeRegistration; import org.spongepowered.api.item.recipe.crafting.RecipeInput; import org.spongepowered.api.item.recipe.single.StoneCutterRecipe; import org.spongepowered.common.inventory.util.InventoryUtil; import org.spongepowered.common.item.recipe.ingredient.IngredientUtil; import org.spongepowered.common.item.util.ItemStackUtil; -import org.spongepowered.common.util.AbstractResourceKeyedBuilder; import org.spongepowered.common.util.Preconditions; import java.util.Objects; import java.util.function.Function; -public final class SpongeStoneCutterRecipeBuilder extends AbstractResourceKeyedBuilder implements +public final class SpongeStoneCutterRecipeBuilder implements StoneCutterRecipe.Builder, StoneCutterRecipe.Builder.ResultStep, StoneCutterRecipe.Builder.EndStep { private ItemStack result; private Ingredient ingredient; private Function resultFunction; private @Nullable String group; - private DataPack pack = DataPacks.RECIPE; private RecipeCategory recipeCategory = RecipeCategory.MISC; // TODO support category @@ -96,15 +90,9 @@ public EndStep group(@Nullable String name) { } @Override - public EndStep pack(final DataPack pack) { - this.pack = pack; - return this; - } - - @Override - public RecipeRegistration build0() { - return new SpongeStonecuttingRecipeRegistration((ResourceLocation) (Object) key, this.group, this.ingredient, - ItemStackUtil.toNative(this.result), this.resultFunction, this.pack, this.recipeCategory); + public StoneCutterRecipe build() { + return (StoneCutterRecipe) new SpongeStonecuttingRecipe(this.group == null ? "": this.group, this.ingredient, + ItemStackUtil.toTemplate(this.result), this.resultFunction); } @Override @@ -113,7 +101,6 @@ public StoneCutterRecipe.Builder reset() { this.resultFunction = null; this.ingredient = null; this.group = null; - this.pack = DataPacks.RECIPE; - return super.reset(); + return this; } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/stonecutting/SpongeStonecuttingRecipe.java b/src/main/java/org/spongepowered/common/item/recipe/stonecutting/SpongeStonecuttingRecipe.java index a2401eadcd8..e332d66ba08 100644 --- a/src/main/java/org/spongepowered/common/item/recipe/stonecutting/SpongeStonecuttingRecipe.java +++ b/src/main/java/org/spongepowered/common/item/recipe/stonecutting/SpongeStonecuttingRecipe.java @@ -24,83 +24,35 @@ */ package org.spongepowered.common.item.recipe.stonecutting; -import static org.spongepowered.common.util.Constants.Recipe.SPONGE_TYPE; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.SingleItemRecipe; import net.minecraft.world.item.crafting.SingleRecipeInput; import net.minecraft.world.item.crafting.StonecutterRecipe; -import org.spongepowered.common.bridge.world.item.crafting.RecipeResultBridge; -import org.spongepowered.common.item.recipe.ResultFunctionRecipe; -import org.spongepowered.common.item.recipe.ingredient.IngredientResultUtil; -import org.spongepowered.common.util.Constants; - -import java.util.Optional; - -public class SpongeStonecuttingRecipe extends StonecutterRecipe implements ResultFunctionRecipe { +import java.util.function.Function; - private static final MapCodec RESULT_CODEC = RecordCodecBuilder.mapCodec( - $$0 -> $$0.group( - BuiltInRegistries.ITEM.byNameCodec().fieldOf(Constants.Recipe.RESULT).forGetter(ItemStack::getItem), - Codec.INT.fieldOf(Constants.Recipe.COUNT).forGetter(ItemStack::getCount) - ) - .apply($$0, ItemStack::new) - ); - public static final MapCodec SPONGE_CODEC = RecordCodecBuilder.mapCodec( - $$1 -> $$1.group( - Codec.STRING.fieldOf(SPONGE_TYPE).forGetter(a -> "custom"), - Codec.STRING.optionalFieldOf("group", "").forGetter(SingleItemRecipe::getGroup), - Ingredient.CODEC_NONEMPTY.fieldOf(Constants.Recipe.STONECUTTING_INGREDIENT).forGetter($$0x -> $$0x.getIngredients().get(0)), - RESULT_CODEC.forGetter($$0x -> ((RecipeResultBridge)$$0x).bridge$result()), - ItemStack.CODEC.optionalFieldOf(Constants.Recipe.SPONGE_RESULT, ItemStack.EMPTY).forGetter($$0x -> ((RecipeResultBridge)$$0x).bridge$spongeResult()), - IngredientResultUtil.CACHED_RESULT_FUNC_CODEC.optionalFieldOf(Constants.Recipe.SPONGE_RESULTFUNCTION).forGetter(ResultFunctionRecipe::resultFunctionId) - ) - .apply($$1, SpongeStonecuttingRecipe::of) - ); +public class SpongeStonecuttingRecipe extends StonecutterRecipe { - private final String resultFunctionId; + private Function resultFunction; - public static SpongeStonecuttingRecipe of(final String spongeType, - final String groupIn, - final Ingredient ingredientIn, - final ItemStack resultIn, - final ItemStack spongeResult, - final Optional resultFunctionId) { - return new SpongeStonecuttingRecipe(groupIn, ingredientIn, spongeResult.isEmpty() ? resultIn : spongeResult, resultFunctionId.orElse(null)); - } - - public SpongeStonecuttingRecipe(final String groupIn, final Ingredient ingredientIn, final ItemStack spongeResult, final String resultFunctionId) { - super(groupIn, ingredientIn, spongeResult); - this.resultFunctionId = resultFunctionId; + public SpongeStonecuttingRecipe( + final String groupIn, + final Ingredient ingredientIn, + final ItemStackTemplate spongeResult, + final Function resultFunction + ) { + super(new CommonInfo(false), ingredientIn, spongeResult); + this.resultFunction = resultFunction; } @Override - public Optional resultFunctionId() { - return Optional.ofNullable(this.resultFunctionId); - } - - @Override - public ItemStack assemble(final SingleRecipeInput $$0, final HolderLookup.Provider $$1) { - if (this.resultFunctionId != null) { - return IngredientResultUtil.cachedResultFunction(this.resultFunctionId).apply($$0); + public ItemStack assemble(final SingleRecipeInput input) { + if (this.resultFunction != null) { + return this.resultFunction.apply(input); } - return super.assemble($$0, $$1); - } - - @Override - public ItemStack getResultItem(final HolderLookup.Provider $$1) { -// if (this.resultFunctionId != null) { -// return ItemStack.EMPTY; -// } - return super.getResultItem($$1); + return super.assemble(input); } } diff --git a/src/main/java/org/spongepowered/common/item/recipe/stonecutting/SpongeStonecuttingRecipeRegistration.java b/src/main/java/org/spongepowered/common/item/recipe/stonecutting/SpongeStonecuttingRecipeRegistration.java deleted file mode 100644 index bb101b4c0b4..00000000000 --- a/src/main/java/org/spongepowered/common/item/recipe/stonecutting/SpongeStonecuttingRecipeRegistration.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.item.recipe.stonecutting; - -import net.minecraft.data.recipes.RecipeCategory; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.RecipeSerializer; -import net.minecraft.world.item.crafting.SingleRecipeInput; -import net.minecraft.world.item.crafting.StonecutterRecipe; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.item.recipe.Recipe; -import org.spongepowered.api.item.recipe.RecipeRegistration; -import org.spongepowered.api.item.recipe.single.StoneCutterRecipe; -import org.spongepowered.common.item.recipe.SpongeRecipeRegistration; - -import java.util.Collections; -import java.util.function.Function; - -public class SpongeStonecuttingRecipeRegistration extends SpongeRecipeRegistration implements - SpongeRecipeRegistration.ResultFunctionRegistration { - - // Vanilla Recipe - private final Ingredient ingredient; - - // Sponge Recipe - private final ItemStack spongeResult; - private Function resultFunction; - - public SpongeStonecuttingRecipeRegistration(ResourceLocation key, String group, Ingredient ingredient, - ItemStack spongeResult, Function resultFunction, - DataPack pack, final RecipeCategory category) { - super(key, group, pack, category, RecipeSerializer.STONECUTTER); - this.ingredient = ingredient; - this.spongeResult = spongeResult; - this.resultFunction = resultFunction; - } - - @Override - public Recipe recipe() { - this.ensureCached(); - if (SpongeRecipeRegistration.isVanillaSerializer(this.spongeResult, this.resultFunction, null, Collections.singleton(this.ingredient))) { - return (StoneCutterRecipe) new StonecutterRecipe(this.group, this.ingredient, this.spongeResult); - } - return (StoneCutterRecipe) new SpongeStonecuttingRecipe( - this.group, this.ingredient, this.spongeResult, this.resultFunction == null ? null : this.key.toString()); - } - - @Override - public Function resultFunction() { - return this.resultFunction; - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/block/entity/JukeboxMixin_ItemStackLike.java b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionFactory.java similarity index 74% rename from src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/block/entity/JukeboxMixin_ItemStackLike.java rename to src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionFactory.java index 23bbd9d7eca..719ab097f17 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/block/entity/JukeboxMixin_ItemStackLike.java +++ b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionFactory.java @@ -22,16 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.itemstacklike.block.entity; +package org.spongepowered.common.item.shield; -import org.spongepowered.api.block.entity.Jukebox; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.api.data.type.ShieldDamageReduction; -@Mixin(value = Jukebox.class, remap = false) -public interface JukeboxMixin_ItemStackLike { +public final class SpongeShieldDamageReductionFactory implements ShieldDamageReduction.Factory { - default void insert(ItemStack disc) { - ((Jukebox) this).insert(disc); + @Override + public ShieldDamageReduction create(final ShieldDamageReduction.MultiplyAdd config) { + return (ShieldDamageReduction) config; } + } diff --git a/src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionMultiplyAddBuilder.java b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionMultiplyAddBuilder.java new file mode 100644 index 00000000000..194978f163d --- /dev/null +++ b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionMultiplyAddBuilder.java @@ -0,0 +1,109 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.item.shield; + +import net.minecraft.core.HolderSet; +import net.minecraft.core.Registry; +import net.minecraft.world.item.component.BlocksAttacks; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.event.cause.entity.damage.DamageType; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.api.tag.Tag; +import org.spongepowered.common.bridge.tags.TagBridge; +import org.spongepowered.common.util.Preconditions; + +import java.util.Optional; +import java.util.Set; + +public final class SpongeShieldDamageReductionMultiplyAddBuilder implements ShieldDamageReduction.MultiplyAdd.Builder { + private HolderSet damageTypes; + private Double horizontalBlockingAngle; + private double base = 0; + private double factor = 0; + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder damageTypes(final Set damageTypes) { + final Registry registry = (Registry) Sponge.server().registry(RegistryTypes.DAMAGE_TYPE); + + this.damageTypes = HolderSet.direct(damageTypes.stream() + .map(dt -> registry.wrapAsHolder((net.minecraft.world.damagesource.DamageType) (Object) dt)) + .toList()); + + return this; + } + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder damageTypes(final Tag tag) { + final Registry registry = (Registry) Sponge.server().registry(RegistryTypes.DAMAGE_TYPE); + final var vanillaTag = ((TagBridge) tag).bridge$asVanillaTag(); + this.damageTypes = registry.getOrThrow(vanillaTag); + + return this; + } + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder horizontalBlockingAngle(final double angle) { + Preconditions.checkArgument(angle > 0, "angle must be positive"); + this.horizontalBlockingAngle = angle; + + return this; + } + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder constantReduction(final double constant) { + this.base = constant; + + return this; + } + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder fractionalReduction(final double fraction) { + this.factor = fraction; + + return this; + } + + @Override + public ShieldDamageReduction.MultiplyAdd build() { + return (ShieldDamageReduction.MultiplyAdd) (Object) new BlocksAttacks.DamageReduction( + this.horizontalBlockingAngle != null ? this.horizontalBlockingAngle.floatValue() : 90, + Optional.ofNullable(this.damageTypes), + (float) this.base, + (float) this.factor + ); + } + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder reset() { + this.damageTypes = null; + this.horizontalBlockingAngle = null; + this.base = 0; + this.factor = 0; + + return this; + } + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/ContainerMixin_ItemStackLike.java b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionFactory.java similarity index 73% rename from src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/ContainerMixin_ItemStackLike.java rename to src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionFactory.java index 41d156060d8..6174a5cb8c1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/ContainerMixin_ItemStackLike.java +++ b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionFactory.java @@ -22,16 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory; +package org.spongepowered.common.item.shield; -import org.spongepowered.api.item.inventory.Container; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; -@Mixin(value = Container.class, remap = false) -public interface ContainerMixin_ItemStackLike { +public final class SpongeShieldItemDamageFunctionFactory implements ShieldItemDamageFunction.Factory { - default boolean setCursor(ItemStack item) { - return ((Container) this).setCursor(item); + @Override + public ShieldItemDamageFunction create(final ShieldItemDamageFunction.MultiplyAdd config) { + return (ShieldItemDamageFunction) config; } + } diff --git a/src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionMultiplyAddBuilder.java b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionMultiplyAddBuilder.java new file mode 100644 index 00000000000..cb241ad33f6 --- /dev/null +++ b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionMultiplyAddBuilder.java @@ -0,0 +1,76 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.item.shield; + +import net.minecraft.world.item.component.BlocksAttacks; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; +import org.spongepowered.common.util.Preconditions; + +public final class SpongeShieldItemDamageFunctionMultiplyAddBuilder implements ShieldItemDamageFunction.MultiplyAdd.Builder { + private double minAttackDamage = 0; + private double constantDamage = 0; + private double fractionalDamage = 0; + + @Override + public ShieldItemDamageFunction.MultiplyAdd.Builder minAttackDamage(final double minDamage) { + Preconditions.checkArgument(minDamage >= 0, "minAttackDamage must not be negative"); + this.minAttackDamage = minDamage; + + return this; + } + + @Override + public ShieldItemDamageFunction.MultiplyAdd.Builder constantDamage(final double constantDamage) { + this.constantDamage = constantDamage; + + return this; + } + + @Override + public ShieldItemDamageFunction.MultiplyAdd.Builder fractionalDamage(final double fractionalDamage) { + this.fractionalDamage = fractionalDamage; + + return this; + } + + @Override + public ShieldItemDamageFunction.MultiplyAdd build() { + return (ShieldItemDamageFunction.MultiplyAdd) (Object) new BlocksAttacks.ItemDamageFunction( + (float) this.minAttackDamage, + (float) this.constantDamage, + (float) this.fractionalDamage + ); + } + + @Override + public ShieldItemDamageFunction.MultiplyAdd.Builder reset() { + this.minAttackDamage = 0; + this.constantDamage = 0; + this.fractionalDamage = 0; + + return this; + } + +} diff --git a/src/main/java/org/spongepowered/common/item/util/ItemStackUtil.java b/src/main/java/org/spongepowered/common/item/util/ItemStackUtil.java index 8fe0cb02211..c3ff825ec3c 100644 --- a/src/main/java/org/spongepowered/common/item/util/ItemStackUtil.java +++ b/src/main/java/org/spongepowered/common/item/util/ItemStackUtil.java @@ -24,6 +24,11 @@ */ package org.spongepowered.common.item.util; +import net.minecraft.core.Holder; +import net.minecraft.core.component.DataComponentPatch; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStackTemplate; +import net.minecraft.world.item.Items; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.item.inventory.ItemStackLike; @@ -47,6 +52,21 @@ public static net.minecraft.world.item.ItemStack toNative(final @Nullable ItemSt throw new NativeStackException("The supplied item stack was not native to the current platform"); } + public static ItemStackTemplate toTemplate(final @Nullable ItemStackLike stack) { + if (stack == null) { + return new ItemStackTemplate(Items.AIR); + } + return switch ((Object) stack) { + case net.minecraft.world.item.ItemStack itemStack: + yield new ItemStackTemplate(itemStack.typeHolder(), itemStack.getCount(), itemStack.getComponentsPatch()); + case SpongeItemStackSnapshot snapshot: + final var direct = Holder.direct(((Item) stack.type())); + yield new ItemStackTemplate(direct, snapshot.quantity(), snapshot.getComponentsPatch()); + default: + throw new NativeStackException("The supplied item stack was not native to the current platform"); + }; + } + /** * Converts a List of SpongeAPI ItemStacks to an Array of native ItemStacks * @@ -67,8 +87,8 @@ public static net.minecraft.world.item.ItemStack[] fromSnapshotToNative(List fromSnapshotToNativeList(List items) { - return items.stream().map(ItemStackUtil::fromSnapshotToNative).toList(); + public static List toTemplate(List items) { + return items.stream().map(ItemStackUtil::toTemplate).toList(); } public static ItemStack fromNative(net.minecraft.world.item.ItemStack stack) { @@ -78,6 +98,10 @@ public static ItemStack fromNative(net.minecraft.world.item.ItemStack stack) { throw new NativeStackException("The supplied native item stack was not compatible with the target environment"); } + public static ItemStack fromTemplate(ItemStackTemplate template) { + return ItemStackUtil.fromNative(template.apply(DataComponentPatch.EMPTY)); + } + /** * Converts an Array of native ItemStacks to a List of SpongeAPI ItemStacks * @@ -169,6 +193,10 @@ private static net.minecraft.world.item.ItemStack underlyingNativeStack(final It return ItemStackUtil.cloneDefensiveNative(stack.asMutableCopy()); } + public static ItemStackSnapshot snapshotOf(ItemStackTemplate template) { + return template == null ? ItemStackSnapshot.empty() : ItemStackUtil.snapshotOf(template.apply(DataComponentPatch.EMPTY)); + } + public static ItemStackSnapshot snapshotOf(net.minecraft.world.item.ItemStack itemStack) { if (itemStack == null) { return ItemStackSnapshot.empty(); @@ -180,7 +208,7 @@ public static ItemStackSnapshot snapshotOf(@Nullable ItemStackLike itemStack) { return itemStack == null ? ItemStackSnapshot.empty() : itemStack.isEmpty() ? ItemStackSnapshot.empty() : itemStack.asImmutable(); } - public static List snapshotOf(final List items) { + public static List snapshotOf(final List items) { return items.stream().map(ItemStackUtil::snapshotOf).toList(); } @@ -197,6 +225,10 @@ public static net.minecraft.world.item.ItemStack fromLikeToNative(@Nullable Item return itemStack == null ? ItemStackUtil.emptyNative() : itemStack.isEmpty() ? ItemStackUtil.emptyNative() : ItemStackUtil.toNative(itemStack.asMutable()); } + public static net.minecraft.world.item.ItemStack fromLikeToNativeCopy(@Nullable ItemStackLike itemStack) { + return itemStack == null ? ItemStackUtil.emptyNative() : itemStack.isEmpty() ? ItemStackUtil.emptyNative() : ItemStackUtil.toNative(itemStack.asMutableCopy()); + } + public static ItemStack empty() { return ItemStackUtil.fromNative(net.minecraft.world.item.ItemStack.EMPTY); } diff --git a/src/main/java/org/spongepowered/common/launch/Launch.java b/src/main/java/org/spongepowered/common/launch/Launch.java index ab1684504c3..a541ec25f54 100644 --- a/src/main/java/org/spongepowered/common/launch/Launch.java +++ b/src/main/java/org/spongepowered/common/launch/Launch.java @@ -25,6 +25,7 @@ package org.spongepowered.common.launch; import com.google.inject.Injector; +import com.google.inject.Module; import com.google.inject.Stage; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -33,11 +34,13 @@ import org.spongepowered.asm.mixin.MixinEnvironment; import org.spongepowered.common.applaunch.plugin.PluginPlatform; import org.spongepowered.common.launch.plugin.SpongePluginManager; +import org.spongepowered.common.launch.plugin.loader.PluginCandidateFactory; import org.spongepowered.plugin.PluginContainer; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; public abstract class Launch { @@ -47,6 +50,7 @@ public abstract class Launch { protected final PluginPlatform pluginPlatform; private final Logger logger; private final List launcherPlugins; + private final PluginCandidateFactory pluginFactory; private PluginContainer minecraftPlugin, apiPlugin, commonPlugin; private Lifecycle lifecycle; @@ -54,6 +58,7 @@ protected Launch(final PluginPlatform pluginPlatform) { this.logger = LogManager.getLogger("launch"); this.pluginPlatform = pluginPlatform; this.launcherPlugins = new ArrayList<>(); + this.pluginFactory = new PluginCandidateFactory(pluginPlatform.environment()); } @SuppressWarnings("unchecked") @@ -95,10 +100,14 @@ public final Logger logger() { return this.logger; } - public PluginPlatform pluginPlatform() { + public final PluginPlatform pluginPlatform() { return this.pluginPlatform; } + public final PluginCandidateFactory pluginFactory() { + return this.pluginFactory; + } + public abstract Stage injectionStage(); public final boolean developerEnvironment() { @@ -159,4 +168,8 @@ public final void auditMixins() { } public abstract Injector createInjector(); + + public Optional modModule(final PluginContainer plugin) { + return Optional.empty(); + } } diff --git a/src/main/java/org/spongepowered/common/launch/Lifecycle.java b/src/main/java/org/spongepowered/common/launch/Lifecycle.java index 8a0d4536500..20d0d6de404 100644 --- a/src/main/java/org/spongepowered/common/launch/Lifecycle.java +++ b/src/main/java/org/spongepowered/common/launch/Lifecycle.java @@ -25,13 +25,15 @@ package org.spongepowered.common.launch; import com.google.inject.Injector; -import net.minecraft.core.RegistryAccess; -import net.minecraft.server.RegistryLayer; import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.permissions.PermissionLevel; import net.minecraft.world.level.WorldDataConfiguration; import org.spongepowered.api.Client; import org.spongepowered.api.Engine; -import org.spongepowered.api.Server; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryHolder; + +import java.util.stream.Stream; /** @@ -50,9 +52,10 @@ public interface Lifecycle { void callRegisterBuilderEvent(); void establishEarlyGlobalRegistries(); - void finalizeEarlyGlobalRegistries(); - void establishGlobalRegistries(RegistryAccess.Frozen registryAccess, final RegistryLayer layer); + void establishGlobalRegistries(); + + void endEstablishGlobalRegistries(); void establishDataProviders(); @@ -64,13 +67,17 @@ public interface Lifecycle { void establishGameServices(); - void establishServerServices(); + void establishServerServices(ResourceManager resourceManager, PermissionLevel functionsPermissionLevel); void establishServerFeatures(); void callConstructEvent(); - void establishServerRegistries(Server server); + void beginEstablishServerRegistries(RegistryHolder server); + + void processServerRegistries(RegistryHolder server, Stream> registries); + + void endEstablishServerRegistries(RegistryHolder server); void establishClientRegistries(Client client); diff --git a/src/main/java/org/spongepowered/common/launch/plugin/SpongePluginContainer.java b/src/main/java/org/spongepowered/common/launch/plugin/SpongePluginContainer.java index 7d6b13b7cda..76737e38e0d 100644 --- a/src/main/java/org/spongepowered/common/launch/plugin/SpongePluginContainer.java +++ b/src/main/java/org/spongepowered/common/launch/plugin/SpongePluginContainer.java @@ -25,11 +25,65 @@ package org.spongepowered.common.launch.plugin; import com.google.inject.Injector; -import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.api.Sponge; +import org.spongepowered.common.inject.plugin.PluginGuice; +import org.spongepowered.plugin.builtin.StandardPluginContainer; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.metadata.model.PluginEntrypoints; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; -public interface SpongePluginContainer extends PluginContainer { +@SuppressWarnings("deprecation") // registerListeners +public class SpongePluginContainer extends StandardPluginContainer { + private Object instance = this; + private Injector injector; - Optional injector(); + public SpongePluginContainer(final PluginResource resource, final PluginMetadata metadata) { + super(resource, metadata); + } + + @SuppressWarnings("removal") + @Override + public Object instance() { + return this.instance; + } + + public Optional injector() { + return Optional.ofNullable(this.injector); + } + + public void loadMainEntrypoints() throws Exception { + final List> pluginClasses = new ArrayList<>(); + for (final String className : this.metadata().entrypoints().main()) { + pluginClasses.add(Class.forName(className)); + } + + final Injector pluginInjector = PluginGuice.createMain(this, pluginClasses); + this.injector = pluginInjector; + + for (final Class pluginClass : pluginClasses) { + final Object plugin = pluginInjector.getInstance(pluginClass); + if (this.instance == this) { + this.instance = plugin; + } + Sponge.eventManager().registerListeners(this, plugin); + } + } + + public void loadSidedEntrypoints(boolean server) throws Exception { + final List> pluginClasses = new ArrayList<>(); + final PluginEntrypoints entrypoints = this.metadata().entrypoints(); + for (final String className : server ? entrypoints.server() : entrypoints.client()) { + pluginClasses.add(Class.forName(className)); + } + + final Injector pluginInjector = PluginGuice.createChild(this, pluginClasses); + for (final Class pluginClass : pluginClasses) { + final Object plugin = pluginInjector.getInstance(pluginClass); + Sponge.eventManager().registerListeners(this, plugin); + } + } } diff --git a/src/main/java/org/spongepowered/common/launch/plugin/loader/JavaPluginLoader.java b/src/main/java/org/spongepowered/common/launch/plugin/loader/JavaPluginLoader.java new file mode 100644 index 00000000000..80c76b9809d --- /dev/null +++ b/src/main/java/org/spongepowered/common/launch/plugin/loader/JavaPluginLoader.java @@ -0,0 +1,55 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.launch.plugin.loader; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.spongepowered.common.launch.plugin.SpongePluginContainer; +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.PluginLoader; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; + +public final class JavaPluginLoader implements PluginLoader { + private static final ArtifactVersion version = new DefaultArtifactVersion("1.0"); + + @Override + public ArtifactVersion version() { + return JavaPluginLoader.version; + } + + @Override + public String name() { + return "java_plain"; + } + + @Override + public PluginContainer loadPlugin(Environment environment, PluginResource resource, PluginMetadata metadata) throws Exception { + final SpongePluginContainer container = new SpongePluginContainer(resource, metadata); + container.loadMainEntrypoints(); + return container; + } +} diff --git a/src/main/java/org/spongepowered/common/launch/plugin/loader/PluginCandidate.java b/src/main/java/org/spongepowered/common/launch/plugin/loader/PluginCandidate.java new file mode 100644 index 00000000000..5a0c28af244 --- /dev/null +++ b/src/main/java/org/spongepowered/common/launch/plugin/loader/PluginCandidate.java @@ -0,0 +1,38 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.launch.plugin.loader; + +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.PluginLoader; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; + +public record PluginCandidate(Environment environment, PluginResource resource, PluginMetadata metadata, PluginLoader loader) { + + public PluginContainer load() throws Exception { + return this.loader.loadPlugin(this.environment, this.resource, this.metadata); + } +} diff --git a/src/main/java/org/spongepowered/common/launch/plugin/loader/PluginCandidateFactory.java b/src/main/java/org/spongepowered/common/launch/plugin/loader/PluginCandidateFactory.java new file mode 100644 index 00000000000..02f12484517 --- /dev/null +++ b/src/main/java/org/spongepowered/common/launch/plugin/loader/PluginCandidateFactory.java @@ -0,0 +1,66 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.launch.plugin.loader; + +import org.spongepowered.common.applaunch.plugin.PluginServiceLoader; +import org.spongepowered.common.applaunch.plugin.VersionChecker; +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.PluginLoader; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; + +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.ServiceLoader; + +public final class PluginCandidateFactory extends PluginServiceLoader { + private final Map loaders = new HashMap<>(); + + public PluginCandidateFactory(final Environment environment) { + super(environment); + for (final PluginLoader loader : this.loadServices("plugin loader", PluginLoader.class)) { + this.loaders.put(loader.name(), loader); + } + } + + @Override + protected ServiceLoader newServiceLoader(final Class serviceClass) { + return ServiceLoader.load(serviceClass); + } + + public PluginCandidate create(final PluginResource resource, final PluginMetadata metadata) { + final PluginLoaderSpecification loaderSpec = metadata.loader(); + final PluginLoader loader = this.loaders.get(loaderSpec.name()); + if (loader == null) { + throw new NoSuchElementException("Cannot find loader " + loaderSpec.name()); + } + if (!VersionChecker.check(loaderSpec.version(), loader.version())) { + throw new IllegalArgumentException("Expected loader " + loader.name() + " version " + loaderSpec.version() + " but found " + loaderSpec.version()); + } + return new PluginCandidate(this.environment, resource, metadata, loader); + } +} diff --git a/src/main/java/org/spongepowered/common/map/SpongeMapStorage.java b/src/main/java/org/spongepowered/common/map/SpongeMapStorage.java index e9556c58dd5..f7a3408423f 100644 --- a/src/main/java/org/spongepowered/common/map/SpongeMapStorage.java +++ b/src/main/java/org/spongepowered/common/map/SpongeMapStorage.java @@ -34,11 +34,10 @@ import org.spongepowered.api.map.MapStorage; import org.spongepowered.api.world.DefaultWorldKeys; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.bridge.map.MapIdTrackerBridge; -import org.spongepowered.common.bridge.world.level.storage.PrimaryLevelDataBridge; +import org.spongepowered.common.accessor.world.level.saveddata.maps.MapIndexAccessor; import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.util.Constants; +import org.spongepowered.common.world.server.SpongeMapUUIDData; import java.util.Collection; import java.util.HashMap; @@ -64,8 +63,7 @@ public Collection allMapInfos() { final Set mapInfos = new HashSet<>(); final ServerLevel defaultWorld = (ServerLevel) Sponge.server().worldManager().world(DefaultWorldKeys.DEFAULT).get(); - final int highestId = ((MapIdTrackerBridge) defaultWorld.getDataStorage() - .computeIfAbsent(MapIndex.factory(), Constants.Map.MAP_INDEX_DATA_NAME)).bridge$getHighestMapId().orElse(-1); + final int highestId = ((MapIndexAccessor) defaultWorld.getDataStorage().computeIfAbsent(MapIndex.TYPE)).accessor$getLastIndexID(); for (int i = 0; i <= highestId; i++) { final @Nullable MapInfo mapInfo = (MapInfo) defaultWorld.getMapData(new MapId(i)); if (mapInfo == null) { @@ -122,7 +120,8 @@ public void addMapInfo(final MapInfo mapInfo) { private void ensureHasMapUUIDIndex() { if (this.mapIdUUIDIndex == null) { - this.mapIdUUIDIndex = ((PrimaryLevelDataBridge) Sponge.server().worldManager().world(DefaultWorldKeys.DEFAULT).get().properties()).bridge$getMapUUIDIndex(); + this.mapIdUUIDIndex = SpongeCommon.server().getDataStorage() + .computeIfAbsent(SpongeMapUUIDData.TYPE).mapUUIDIndex(); } } } diff --git a/src/main/java/org/spongepowered/common/map/decoration/SpongeMapDecorationBuilder.java b/src/main/java/org/spongepowered/common/map/decoration/SpongeMapDecorationBuilder.java index f66e259aeba..dc088ee0b29 100644 --- a/src/main/java/org/spongepowered/common/map/decoration/SpongeMapDecorationBuilder.java +++ b/src/main/java/org/spongepowered/common/map/decoration/SpongeMapDecorationBuilder.java @@ -28,7 +28,7 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.data.persistence.DataQuery; @@ -125,7 +125,7 @@ public MapDecoration.Builder fromContainer(final DataView container) { final byte rot = this.getByteFromContainer(container, Constants.Map.DECORATION_ROTATION); - final var mcType = BuiltInRegistries.MAP_DECORATION_TYPE.get((ResourceLocation) (Object) type); + final var mcType = BuiltInRegistries.MAP_DECORATION_TYPE.getValue((Identifier) (Object) type); if (mcType == null) { throw new IllegalStateException("Missing a MapDecorationType, could not find one for Minecraft's MapDecoration.Type: " + type); } diff --git a/src/main/java/org/spongepowered/common/network/channel/ChannelUtils.java b/src/main/java/org/spongepowered/common/network/channel/ChannelUtils.java index 57514e417a1..f00b9f568f3 100644 --- a/src/main/java/org/spongepowered/common/network/channel/ChannelUtils.java +++ b/src/main/java/org/spongepowered/common/network/channel/ChannelUtils.java @@ -25,7 +25,7 @@ package org.spongepowered.common.network.channel; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.spongepowered.api.Sponge; import org.spongepowered.common.hooks.PlatformHooks; import org.spongepowered.common.util.Constants; @@ -34,7 +34,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public final class ChannelUtils { - public static final CustomPacketPayload.Type REGISTER = new CustomPacketPayload.Type<>((ResourceLocation) (Object) Constants.Channels.REGISTER_KEY); + public static final CustomPacketPayload.Type REGISTER = new CustomPacketPayload.Type<>((Identifier) (Object) Constants.Channels.REGISTER_KEY); public static ArrayList spongeChannelCodecs(final int maxPayloadSize) { ArrayList channels = new ArrayList<>(); diff --git a/src/main/java/org/spongepowered/common/network/channel/PacketSender.java b/src/main/java/org/spongepowered/common/network/channel/PacketSender.java index ffa5b9a77bc..443cce8512b 100644 --- a/src/main/java/org/spongepowered/common/network/channel/PacketSender.java +++ b/src/main/java/org/spongepowered/common/network/channel/PacketSender.java @@ -24,8 +24,9 @@ */ package org.spongepowered.common.network.channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; import net.minecraft.network.Connection; -import net.minecraft.network.PacketSendListener; import net.minecraft.network.protocol.Packet; import net.minecraft.util.thread.BlockableEventLoop; import org.checkerframework.checker.nullness.qual.Nullable; @@ -59,7 +60,7 @@ public static void sendTo(final EngineConnection connection, final Packet pac }); } - public static final class SpongePacketSendListener implements PacketSendListener { + public static final class SpongePacketSendListener implements ChannelFutureListener { private final BlockableEventLoop executor; private final Consumer<@Nullable Throwable> listener; @@ -71,6 +72,15 @@ public SpongePacketSendListener(final EngineConnectionSide this.listener.accept(throwable)); } + + @Override + public void operationComplete(ChannelFuture future) { + this.executor.execute(() -> { + if (!future.isSuccess()) { + this.listener.accept(future.cause()); + } + }); + } } private PacketSender() { diff --git a/src/main/java/org/spongepowered/common/network/channel/SpongeChannel.java b/src/main/java/org/spongepowered/common/network/channel/SpongeChannel.java index 0dabdf2ae4d..4430e5be015 100644 --- a/src/main/java/org/spongepowered/common/network/channel/SpongeChannel.java +++ b/src/main/java/org/spongepowered/common/network/channel/SpongeChannel.java @@ -26,7 +26,7 @@ import com.google.common.collect.Multimap; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.Nullable; @@ -69,7 +69,7 @@ public SpongeChannel(final int type, final ResourceKey key, final SpongeChannelM this.key = key; this.manager = manager; this.logger = LogManager.getLogger("channel/" + key.formatted()); - this.payloadType = new CustomPacketPayload.Type<>((ResourceLocation) (Object) key); + this.payloadType = new CustomPacketPayload.Type<>((Identifier) (Object) key); } public int getType() { diff --git a/src/main/java/org/spongepowered/common/network/channel/SpongeChannelManager.java b/src/main/java/org/spongepowered/common/network/channel/SpongeChannelManager.java index 30ab1bb0711..5f1a74b15bb 100644 --- a/src/main/java/org/spongepowered/common/network/channel/SpongeChannelManager.java +++ b/src/main/java/org/spongepowered/common/network/channel/SpongeChannelManager.java @@ -31,7 +31,7 @@ import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.network.protocol.login.custom.CustomQueryPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; @@ -172,8 +172,8 @@ public CompletableFuture requestClientType(final EngineConnection connecti final Packet mcPacket = PacketUtil.createLoginPayloadRequest(new CustomQueryPayload() { @Override - public ResourceLocation id() { - return (ResourceLocation) (Object) Constants.Channels.SPONGE_CLIENT_TYPE; + public Identifier id() { + return (Identifier) (Object) Constants.Channels.SPONGE_CLIENT_TYPE; } @Override @@ -217,8 +217,8 @@ public CompletableFuture sendLoginChannelRegistry(final EngineConnection c final Consumer consumer = this.encodeChannelRegistry(); final Packet mcPacket = PacketUtil.createLoginPayloadRequest(new CustomQueryPayload() { @Override - public ResourceLocation id() { - return (ResourceLocation) (Object) Constants.Channels.SPONGE_CHANNEL_REGISTRY; + public Identifier id() { + return (Identifier) (Object) Constants.Channels.SPONGE_CHANNEL_REGISTRY; } @Override @@ -288,7 +288,7 @@ private void handleChannelRegistry(final EngineConnection connection, final Chan } } - public boolean handlePlayPayload(final EngineConnection connection, final EngineConnectionState state, final ResourceLocation channelKey, final Consumer payload) { + public boolean handlePlayPayload(final EngineConnection connection, final EngineConnectionState state, final Identifier channelKey, final Consumer payload) { final ChannelBuf buf = this.bufferAllocator.buffer(); payload.accept((FriendlyByteBuf) buf); @@ -334,7 +334,7 @@ private boolean handlePlayPayload(final EngineConnection connection, final Engin } } - public boolean handleLoginRequestPayload(final EngineConnection connection, final EngineConnectionState state, final ResourceLocation channelKey, final int transactionId, final Consumer payload) { + public boolean handleLoginRequestPayload(final EngineConnection connection, final EngineConnectionState state, final Identifier channelKey, final int transactionId, final Consumer payload) { // Server -> Client request final ChannelBuf buf = this.bufferAllocator.buffer(); payload.accept((FriendlyByteBuf) buf); diff --git a/src/main/java/org/spongepowered/common/network/channel/SpongeChannelPayload.java b/src/main/java/org/spongepowered/common/network/channel/SpongeChannelPayload.java index e76845f650c..d03e2351cc9 100644 --- a/src/main/java/org/spongepowered/common/network/channel/SpongeChannelPayload.java +++ b/src/main/java/org/spongepowered/common/network/channel/SpongeChannelPayload.java @@ -30,12 +30,12 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.network.protocol.login.custom.CustomQueryAnswerPayload; import net.minecraft.network.protocol.login.custom.CustomQueryPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.function.Consumer; -public record SpongeChannelPayload(@Nullable Type type, @Nullable ResourceLocation id, Consumer consumer) implements CustomPacketPayload, CustomQueryPayload, CustomQueryAnswerPayload { +public record SpongeChannelPayload(@Nullable Type type, @Nullable Identifier id, Consumer consumer) implements CustomPacketPayload, CustomQueryPayload, CustomQueryAnswerPayload { public static StreamCodec streamCodec(final Type type, final int maxPayloadSize) { return CustomPacketPayload.codec( @@ -59,7 +59,7 @@ public Type type() { } @Override - public ResourceLocation id() { + public Identifier id() { return this.id; } @@ -67,7 +67,7 @@ public static SpongeChannelPayload fromType(final Type consumer) { + public static SpongeChannelPayload fromId(final Identifier id, final Consumer consumer) { return new SpongeChannelPayload(null, id, consumer); } diff --git a/src/main/java/org/spongepowered/common/network/channel/packet/SpongeBasicPacketChannel.java b/src/main/java/org/spongepowered/common/network/channel/packet/SpongeBasicPacketChannel.java index 68ad9ce8eb1..f5349bd002c 100644 --- a/src/main/java/org/spongepowered/common/network/channel/packet/SpongeBasicPacketChannel.java +++ b/src/main/java/org/spongepowered/common/network/channel/packet/SpongeBasicPacketChannel.java @@ -26,7 +26,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.login.custom.CustomQueryPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.network.ClientSideConnection; @@ -93,8 +93,8 @@ private

, R extends Packet> void sendRequestTo(final final net.minecraft.network.protocol.Packet mcPacket = PacketUtil.createLoginPayloadRequest(new CustomQueryPayload() { @Override - public ResourceLocation id() { - return (ResourceLocation) (Object) Constants.Channels.FML_LOGIN_WRAPPER_CHANNEL; + public Identifier id() { + return (Identifier) (Object) Constants.Channels.FML_LOGIN_WRAPPER_CHANNEL; } @Override @@ -139,8 +139,8 @@ private CompletableFuture sendNormalTo(final EngineConnection connection, final net.minecraft.network.protocol.Packet mcPacket = PacketUtil.createLoginPayloadRequest(new CustomQueryPayload() { @Override - public ResourceLocation id() { - return (ResourceLocation) (Object) key; + public Identifier id() { + return (Identifier) (Object) key; } @Override @@ -180,12 +180,14 @@ public CompletableFuture sendTo(final EngineConnection connection, final P } } + @SuppressWarnings("deprecated") @Override public CompletableFuture sendToServer(final RequestPacket packet) { throw new UnsupportedOperationException( "Request packets from the client to server are currently not supported for basic packet channels."); } + @SuppressWarnings("deprecation") @Override public CompletableFuture sendToServer(final Packet packet) { throw new UnsupportedOperationException( diff --git a/src/main/java/org/spongepowered/common/network/channel/packet/SpongePacketChannel.java b/src/main/java/org/spongepowered/common/network/channel/packet/SpongePacketChannel.java index 13011064d58..7ec82bee974 100644 --- a/src/main/java/org/spongepowered/common/network/channel/packet/SpongePacketChannel.java +++ b/src/main/java/org/spongepowered/common/network/channel/packet/SpongePacketChannel.java @@ -26,7 +26,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.login.custom.CustomQueryPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.network.EngineConnection; @@ -122,8 +122,8 @@ private

, R extends Packet> void sendRequestPacketTo( final ResourceKey key = this.key(); mcPacketSupplier = () -> PacketUtil.createLoginPayloadRequest(new CustomQueryPayload() { @Override - public ResourceLocation id() { - return (ResourceLocation) (Object) key; + public Identifier id() { + return (Identifier) (Object) key; } @Override @@ -187,8 +187,8 @@ private

, R extends Packet> void sendResponsePacketTo final ResourceKey key = this.key(); mcPacketSupplier = () -> PacketUtil.createLoginPayloadRequest(new CustomQueryPayload() { @Override - public ResourceLocation id() { - return (ResourceLocation) (Object) key; + public Identifier id() { + return (Identifier) (Object) key; } @Override @@ -216,8 +216,8 @@ public void write(FriendlyByteBuf var1) { final ResourceKey key = this.key(); mcPacketSupplier = () -> PacketUtil.createLoginPayloadRequest(new CustomQueryPayload() { @Override - public ResourceLocation id() { - return (ResourceLocation) (Object) key; + public Identifier id() { + return (Identifier) (Object) key; } @Override @@ -269,8 +269,8 @@ private

extends ProcessorType { - - // @formatter:off - @Shadow MapCodec

shadow$codec(); - // @formatter:on +public final class SpongeProcessorFactory implements Processor.Factory { @Override - default Processor configure(final DataView dataView) throws IOException { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, SpongeCommon.server().registryAccess()); - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(dataView)); - final P processor = this.shadow$codec().codec().parse(ops, json).getOrThrow(); + public Processor parse(final RegistryHolder registries, final ResourceKey id, final DataView config) throws IOException { + Objects.requireNonNull(registries, "registries"); + Objects.requireNonNull(id, "id"); + Objects.requireNonNull(config, "config"); + + final SpongeRegistryHolder spongeHolder = (SpongeRegistryHolder) registries; + final HolderLookup.RegistryLookup> lookup = + spongeHolder.lookupOrThrow(Registries.STRUCTURE_PROCESSOR); + + final Identifier identifier = Identifier.fromNamespaceAndPath(id.namespace(), id.value()); + final net.minecraft.resources.ResourceKey> vanillaKey = + net.minecraft.resources.ResourceKey.create(Registries.STRUCTURE_PROCESSOR, identifier); + final MapCodec codec = lookup.get(vanillaKey) + .orElseThrow(() -> new IOException("Unknown structure processor id: " + id)) + .value(); + + final RegistryOps ops = spongeHolder.createSerializationContext(JsonOps.INSTANCE); + final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(config)); + final StructureProcessor processor = codec.codec().parse(ops, json).getOrThrow( + message -> new IllegalArgumentException("Failed to parse processor " + id + ": " + message)); return (Processor) processor; } } diff --git a/src/main/java/org/spongepowered/common/event/lifecycle/RegisterDataPackValueEventImpl.java b/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeProcessorListBuilder.java similarity index 53% rename from src/main/java/org/spongepowered/common/event/lifecycle/RegisterDataPackValueEventImpl.java rename to src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeProcessorListBuilder.java index 467c20abb3e..0afee0aebd5 100644 --- a/src/main/java/org/spongepowered/common/event/lifecycle/RegisterDataPackValueEventImpl.java +++ b/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeProcessorListBuilder.java @@ -22,43 +22,45 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.event.lifecycle; +package org.spongepowered.common.world.generation.structure.jigsaw; -import com.google.gson.JsonElement; -import org.spongepowered.api.Game; -import org.spongepowered.api.datapack.DataPackEntry; -import org.spongepowered.api.datapack.DataPackType; -import org.spongepowered.api.event.Cause; -import org.spongepowered.api.event.lifecycle.RegisterDataPackValueEvent; -import org.spongepowered.common.datapack.SpongeDataPackType; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.world.generation.structure.jigsaw.Processor; +import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorList; -import java.util.ArrayList; import java.util.List; import java.util.Objects; -public final class RegisterDataPackValueEventImpl> extends AbstractLifecycleEvent.GenericImpl implements RegisterDataPackValueEvent { +public final class SpongeProcessorListBuilder implements ProcessorList.Builder { - private final DataPackType type; - private final List serializables; + @Nullable private StructureProcessorList processorList; - public RegisterDataPackValueEventImpl(final Cause cause, final Game game, final SpongeDataPackType type) { - super(cause, game, type.entryType()); - this.type = type; - this.serializables = new ArrayList<>(); + public SpongeProcessorListBuilder() { + this.reset(); } @Override - public DataPackType type() { - return this.type; + public ProcessorList.Builder fromValues(final List processorList) { + this.processorList = new StructureProcessorList((List) processorList); + return this; + } + + @Override + public ProcessorList.Builder from(final ProcessorList processorList) { + this.processorList = new StructureProcessorList((List) processorList.processors()); + return this; } @Override - public RegisterDataPackValueEvent register(final T serializable) { - this.serializables.add(Objects.requireNonNull(serializable, "serializable")); + public ProcessorList.Builder reset() { + this.processorList = null; return this; } - public List serializables() { - return this.serializables; + @Override + public ProcessorList build() { + Objects.requireNonNull(this.processorList, "processorList"); + return (ProcessorList) this.processorList; } } diff --git a/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeProcessorListTemplate.java b/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeProcessorListTemplate.java deleted file mode 100644 index 03abb6d758c..00000000000 --- a/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeProcessorListTemplate.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.generation.structure.jigsaw; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.RegistryOps; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.world.generation.structure.jigsaw.Processor; -import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorList; -import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorListTemplate; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.List; -import java.util.Objects; -import java.util.function.Function; - -public record SpongeProcessorListTemplate(ResourceKey key, StructureProcessorList representedProcessors, DataPack pack) implements ProcessorListTemplate { - - @Override - public ProcessorList processorList() { - return (ProcessorList) this.representedProcessors; - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeProcessorListTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized ProcessorList:\n" + serialized, e); - } - } - - public static JsonElement encode(final ProcessorListTemplate template, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return StructureProcessorType.LIST_OBJECT_CODEC.encodeStart(ops, ((StructureProcessorList) template.processorList())).getOrThrow(); - } - - public static StructureProcessorList decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return StructureProcessorType.LIST_OBJECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static SpongeProcessorListTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement json, final RegistryAccess registryAccess) { - final StructureProcessorList parsed = SpongeProcessorListTemplate.decode(json, registryAccess); - return new SpongeProcessorListTemplate(key, parsed, pack); - } - - public static final class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - @Nullable private StructureProcessorList processorList; - - public BuilderImpl() { - this.reset(); - } - - @Override - public Function valueExtractor() { - return value -> (ProcessorList) ((SpongeProcessorListTemplate) value).representedProcessors; - } - - @Override - public Builder fromValues(final List processorList) { - this.processorList = new StructureProcessorList((List) processorList); - return this; - } - - @Override - public Builder fromValue(final ProcessorList processorList) { - this.processorList = (StructureProcessorList) processorList; - return this; - } - - @Override - public Builder reset() { - this.pack = DataPacks.PROCESSOR_LIST; - this.key = null; - this.processorList = null; - return this; - } - - @Override - public Builder fromDataPack(final DataView datapack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(datapack)); - final StructureProcessorList decoded = SpongeProcessorListTemplate.decode(json, SpongeCommon.server().registryAccess()); - this.fromValue((ProcessorList) decoded); - return this; - } - - @Override - protected SpongeProcessorListTemplate build0() { - Objects.requireNonNull(this.processorList, "processorList"); - return new SpongeProcessorListTemplate(this.key, this.processorList, this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/level/chunk/SpongeEmptyChunk.java b/src/main/java/org/spongepowered/common/world/level/chunk/SpongeEmptyChunk.java index d6efe44e263..f425c3ac256 100644 --- a/src/main/java/org/spongepowered/common/world/level/chunk/SpongeEmptyChunk.java +++ b/src/main/java/org/spongepowered/common/world/level/chunk/SpongeEmptyChunk.java @@ -124,7 +124,7 @@ public Vector3i max() { @Override public Vector3i chunkPosition() { final ChunkPos pos = this.chunk.getPos(); - return new Vector3i(pos.x, 0, pos.z); + return new Vector3i(pos.x(), 0, pos.z()); } @Override diff --git a/src/main/java/org/spongepowered/common/world/level/chunk/SpongeOfflineChunk.java b/src/main/java/org/spongepowered/common/world/level/chunk/SpongeOfflineChunk.java index d5a720b3142..362352822aa 100644 --- a/src/main/java/org/spongepowered/common/world/level/chunk/SpongeOfflineChunk.java +++ b/src/main/java/org/spongepowered/common/world/level/chunk/SpongeOfflineChunk.java @@ -60,7 +60,7 @@ public static OfflineChunk of(final RegionFile regionFile, final ChunkPos pos) { throw new RuntimeException(e); } - return new SpongeOfflineChunk(chunkNbt, pos.x, pos.z); + return new SpongeOfflineChunk(chunkNbt, pos.x(), pos.z()); } @Override diff --git a/src/main/java/org/spongepowered/common/world/level/chunk/storage/SpongeEntityChunk.java b/src/main/java/org/spongepowered/common/world/level/chunk/storage/SpongeEntityChunk.java index 08186f8e900..618b182176a 100644 --- a/src/main/java/org/spongepowered/common/world/level/chunk/storage/SpongeEntityChunk.java +++ b/src/main/java/org/spongepowered/common/world/level/chunk/storage/SpongeEntityChunk.java @@ -25,7 +25,6 @@ package org.spongepowered.common.world.level.chunk.storage; import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.Tuple; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.persistence.DataContainer; @@ -33,6 +32,7 @@ import org.spongepowered.api.entity.EntityType; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.util.AABB; +import org.spongepowered.api.util.Tuple; import org.spongepowered.api.world.chunk.EntityChunk; import org.spongepowered.api.world.volume.stream.StreamOptions; import org.spongepowered.api.world.volume.stream.VolumeStream; @@ -74,7 +74,7 @@ public SpongeEntityChunk(final ServerLevel level, final Vector3i chunkPosition, public Vector3i min() { if (this.blockMin == null) { if (this.chunkLayout == null) { - this.chunkLayout = new SpongeChunkLayout(this.level.getMinBuildHeight(), this.level.getHeight()); + this.chunkLayout = new SpongeChunkLayout(this.level.getMinY(), this.level.getHeight()); } this.blockMin = this.chunkLayout.forceToWorld(this.chunkPosition); } @@ -85,7 +85,7 @@ public Vector3i min() { public Vector3i max() { if (this.blockMax == null) { if (this.chunkLayout == null) { - this.chunkLayout = new SpongeChunkLayout(this.level.getMinBuildHeight(), this.level.getHeight()); + this.chunkLayout = new SpongeChunkLayout(this.level.getMinY(), this.level.getHeight()); } this.blockMax = this.min().add(this.chunkLayout.chunkSize()).sub(1, 1, 1); } diff --git a/src/main/java/org/spongepowered/common/world/portal/SpongeCompositePortalLogic.java b/src/main/java/org/spongepowered/common/world/portal/SpongeCompositePortalLogic.java index 9e5f9112353..f517ca6991c 100644 --- a/src/main/java/org/spongepowered/common/world/portal/SpongeCompositePortalLogic.java +++ b/src/main/java/org/spongepowered/common/world/portal/SpongeCompositePortalLogic.java @@ -27,7 +27,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.portal.DimensionTransition; +import net.minecraft.world.level.portal.TeleportTransition; import org.jetbrains.annotations.Nullable; import org.spongepowered.api.util.Axis; import org.spongepowered.api.world.portal.Portal; @@ -50,7 +50,7 @@ public SpongeCompositePortalLogic(final List p.getPortalDestination(sourceLevel, entity, portalPos)) .filter(Objects::nonNull) diff --git a/src/main/java/org/spongepowered/common/world/portal/SpongeCustomPortalLogic.java b/src/main/java/org/spongepowered/common/world/portal/SpongeCustomPortalLogic.java index 65bf3c1c70d..7e73377ce5f 100644 --- a/src/main/java/org/spongepowered/common/world/portal/SpongeCustomPortalLogic.java +++ b/src/main/java/org/spongepowered/common/world/portal/SpongeCustomPortalLogic.java @@ -27,7 +27,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.portal.DimensionTransition; +import net.minecraft.world.level.portal.TeleportTransition; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.util.Axis; import org.spongepowered.api.world.portal.Portal; @@ -60,7 +60,7 @@ public SpongeCustomPortalLogic(final PortalLogic.PortalExitCalculator calulator, } @Nullable @Override - public DimensionTransition getPortalDestination(final ServerLevel fromLevel, final Entity entity, final BlockPos fromPos) { + public TeleportTransition getPortalDestination(final ServerLevel fromLevel, final Entity entity, final BlockPos fromPos) { final var spongeEntity = (org.spongepowered.api.entity.Entity) entity; // Calculate desired portal location // Then find existing portal or generate if not found @@ -70,14 +70,14 @@ public DimensionTransition getPortalDestination(final ServerLevel fromLevel, fin ).orElse(null); } - private static DimensionTransition generateTransition(final Entity entity, final ServerLocation finalExit) { - return new DimensionTransition( + private static TeleportTransition generateTransition(final Entity entity, final ServerLocation finalExit) { + return new TeleportTransition( (ServerLevel) finalExit.world(), VecHelper.toVanillaVector3d(finalExit.position()), entity.getDeltaMovement(), entity.getYRot(), entity.getXRot(), - DimensionTransition.PLACE_PORTAL_TICKET); + TeleportTransition.PLACE_PORTAL_TICKET); } @Override diff --git a/src/main/java/org/spongepowered/common/world/portal/SpongeEndPlatformGenerator.java b/src/main/java/org/spongepowered/common/world/portal/SpongeEndPlatformGenerator.java index 778a2571374..bd9bac9dc2d 100644 --- a/src/main/java/org/spongepowered/common/world/portal/SpongeEndPlatformGenerator.java +++ b/src/main/java/org/spongepowered/common/world/portal/SpongeEndPlatformGenerator.java @@ -27,6 +27,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.levelgen.feature.EndPlatformFeature; +import net.minecraft.world.phys.Vec3; import org.spongepowered.api.util.Axis; import org.spongepowered.api.world.portal.Portal; import org.spongepowered.api.world.portal.PortalLogic; @@ -43,7 +44,7 @@ public final class SpongeEndPlatformGenerator implements PortalLogic.PortalGener @Override public Optional generatePortal(final ServerLocation location, final Axis axis) { final var level = (ServerLevel) location.world(); - final var bottomCenter = VecHelper.toBlockPos(location.blockPosition()).getBottomCenter(); + final var bottomCenter = Vec3.atBottomCenterOf(VecHelper.toBlockPos(location.blockPosition())); EndPlatformFeature.createEndPlatform(level, BlockPos.containing(bottomCenter).below(), true); return Optional.of(new SpongePortal(location, null)); // TODO set portallogic? } diff --git a/src/main/java/org/spongepowered/common/world/schematic/SchematicTranslator.java b/src/main/java/org/spongepowered/common/world/schematic/SchematicTranslator.java index d3c32a85a77..1a2eb6b61bc 100644 --- a/src/main/java/org/spongepowered/common/world/schematic/SchematicTranslator.java +++ b/src/main/java/org/spongepowered/common/world/schematic/SchematicTranslator.java @@ -128,7 +128,7 @@ public Schematic translate(final DataView unprocessed) throws InvalidDataExcepti final int dataVersion = schematicView.getInt(Constants.Sponge.Schematic.DATA_VERSION).get(); // DataFixer will be able to upgrade entity and tile entity data if and only if we're running a valid server and // the data version is outdated. - final boolean needsFixers = dataVersion < SharedConstants.getCurrentVersion().getDataVersion().getVersion() && SchematicTranslator.VANILLA_FIXER != null; + final boolean needsFixers = dataVersion < SharedConstants.getCurrentVersion().dataVersion().version() && SchematicTranslator.VANILLA_FIXER != null; final DataView updatedView; @@ -327,7 +327,7 @@ private static void deserializeBiomeContainer( .orElseThrow(() -> new InvalidDataException("Missing BiomePalette as required by the schematic spec")); final Set biomeKeys = biomeMap.keys(false); - final Registry biomeRegistry = VolumeStreamUtils.nativeToSpongeRegistry(SpongeCommon.server().registryAccess().registryOrThrow(Registries.BIOME)); + final Registry biomeRegistry = VolumeStreamUtils.nativeToSpongeRegistry(SpongeCommon.vanillaRegistry(Registries.BIOME)); biomePalette = new MutableBimapPalette<>( PaletteTypes.BIOME_PALETTE.get(), biomeRegistry, @@ -420,7 +420,7 @@ public DataView addTo(final Schematic schematic, final DataView data) { data.set(Constants.Sponge.Schematic.LENGTH, (short) length); data.set(Constants.Sponge.Schematic.VERSION, Constants.Sponge.Schematic.CURRENT_VERSION); - data.set(Constants.Sponge.Schematic.DATA_VERSION, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + data.set(Constants.Sponge.Schematic.DATA_VERSION, SharedConstants.getCurrentVersion().dataVersion().version()); for (final DataQuery metaKey : schematic.metadata().keys(false)) { data.set(Constants.Sponge.Schematic.METADATA.then(metaKey), schematic.metadata().get(metaKey).get()); } @@ -454,7 +454,7 @@ public DataView addTo(final Schematic schematic, final DataView data) { final Registry blockRegistry = VolumeStreamUtils.nativeToSpongeRegistry( - SpongeCommon.server().registryAccess().registryOrThrow(Registries.BLOCK)); + SpongeCommon.vanillaRegistry(Registries.BLOCK)); SchematicTranslator.writePaletteToView( blockData, palette, blockRegistry, Constants.Sponge.Schematic.BLOCK_PALETTE, BlockState::type, @@ -506,7 +506,7 @@ public DataView addTo(final Schematic schematic, final DataView data) { // Should never reach here. } - final Registry biomeRegistry = VolumeStreamUtils.nativeToSpongeRegistry(SpongeCommon.server().registryAccess().registryOrThrow(Registries.BIOME)); + final Registry biomeRegistry = VolumeStreamUtils.nativeToSpongeRegistry(SpongeCommon.vanillaRegistry(Registries.BIOME)); SchematicTranslator.writePaletteToView( biomeContainer, biomePalette, biomeRegistry, Constants.Sponge.Schematic.BIOME_PALETTE, diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeLevelMigration.java b/src/main/java/org/spongepowered/common/world/server/SpongeLevelMigration.java new file mode 100644 index 00000000000..bf4362256be --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/server/SpongeLevelMigration.java @@ -0,0 +1,162 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.server; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.common.SpongeCommon; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +/** + * Handles migration of old Sponge world data (UUID, key) from the pre-26.1 format + * to the new per-dimension {@link SpongeRegistryData} SavedData format. + * + *

The migration flow is:

+ *
    + *
  1. {@code FileFixerUpperMixin} extracts UUIDs from old {@code SpongeData} in level.dat + * and per-dimension level.dat files BEFORE vanilla's file fixer rewrites them.
  2. + *
  3. Cached identities are stored via {@link #cacheIdentities(Map)}.
  4. + *
  5. {@code MinecraftServerMixin.loadLevel()} calls {@link #writeCachedRegistryData(Path)} + * AFTER the file fix completes but BEFORE any ServerLevel is created.
  6. + *
+ */ +public final class SpongeLevelMigration { + + // Static fields (not ThreadLocal) because fix() runs on "main" thread + // but loadLevel() runs on "Server thread". + private static volatile @Nullable Map, UUID> cachedIdentities; + private static volatile @Nullable CompoundTag cachedGlobalSpongeData; + + private SpongeLevelMigration() { + } + + public static void cacheIdentities(final Map, UUID> identities) { + cachedIdentities = identities; + } + + public static void cacheGlobalSpongeData(final CompoundTag spongeData) { + cachedGlobalSpongeData = spongeData; + } + + /** + * Writes cached migrated identities to per-dimension {@code data/sponge/registry.dat} files. + * Must be called AFTER FileFixerUpper.fix() completes (directory structure is final) + * and BEFORE any ServerLevel is created (so SavedDataStorage finds files on disk). + */ + public static void writeCachedRegistryData(final Path worldDir) { + final @Nullable Map, UUID> identities = cachedIdentities; + if (identities == null || identities.isEmpty()) { + cachedIdentities = null; + return; + } + cachedIdentities = null; + + for (final var entry : identities.entrySet()) { + final ResourceKey dimKey = entry.getKey(); + final UUID uuid = entry.getValue(); + try { + final Path dimDir = DimensionType.getStorageFolder(dimKey, worldDir); + final Path dataDir = dimDir.resolve("data").resolve("sponge"); + final Path registryFile = dataDir.resolve("registry.dat"); + if (Files.exists(registryFile)) { + continue; + } + Files.createDirectories(dataDir); + final SpongeRegistryData registryData = new SpongeRegistryData(uuid, Optional.of(dimKey)); + final var encoded = SpongeRegistryData.CODEC.encodeStart(NbtOps.INSTANCE, registryData); + if (encoded.isSuccess()) { + final CompoundTag savedDataTag = new CompoundTag(); + savedDataTag.put("data", encoded.getOrThrow()); + NbtUtils.addCurrentDataVersion(savedDataTag); + NbtIo.writeCompressed(savedDataTag, registryFile); + SpongeCommon.logger().info("[Sponge] Wrote migrated {} UUID {} -> {}", dimKey.identifier(), uuid, registryFile); + } + } catch (final IOException e) { + SpongeCommon.logger().warn("[Sponge] Failed to write registry.dat for {}", dimKey.identifier(), e); + } + } + + // Write global Sponge data (MapUUIDs, player-uuid-table) to server-level SavedData + writeGlobalSpongeData(worldDir); + } + + private static void writeGlobalSpongeData(final Path worldDir) { + final @Nullable CompoundTag spongeData = cachedGlobalSpongeData; + cachedGlobalSpongeData = null; + if (spongeData == null) { + return; + } + + final Path dataDir = worldDir.resolve("data").resolve("sponge"); + final Path mapUUIDFile = dataDir.resolve("map_uuids.dat"); + if (Files.exists(mapUUIDFile)) { + return; // Already migrated + } + + // Extract MapUUIDs and player-uuid-table from old SpongeData + final var mapUUIDs = spongeData.getCompound("MapUUIDs").orElse(null); + final var playerTable = spongeData.getList("player-uuid-table").orElse(null); + + // Only write if there's actual data to migrate + final boolean hasMapData = mapUUIDs != null && !mapUUIDs.isEmpty(); + final boolean hasPlayerData = playerTable != null && !playerTable.isEmpty(); + if (!hasMapData && !hasPlayerData) { + return; + } + + try { + Files.createDirectories(dataDir); + // Write as a raw compound since the old format doesn't match the new codec exactly. + // The SpongeMapUUIDData.CODEC will read the proper format; here we just preserve the raw data + // for potential manual migration. The new SavedData will be created fresh on first access. + final CompoundTag savedDataTag = new CompoundTag(); + final CompoundTag data = new CompoundTag(); + if (hasMapData) { + data.put("legacy_map_uuids", mapUUIDs); + } + if (hasPlayerData) { + data.put("legacy_player_uuids", playerTable); + } + savedDataTag.put("data", data); + NbtUtils.addCurrentDataVersion(savedDataTag); + NbtIo.writeCompressed(savedDataTag, mapUUIDFile); + SpongeCommon.logger().info("[Sponge] Preserved global MapUUIDs/player-uuid-table -> {}", mapUUIDFile); + } catch (final IOException e) { + SpongeCommon.logger().warn("[Sponge] Failed to write global Sponge data", e); + } + } +} diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeMapUUIDData.java b/src/main/java/org/spongepowered/common/world/server/SpongeMapUUIDData.java new file mode 100644 index 00000000000..01fe1358346 --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/server/SpongeMapUUIDData.java @@ -0,0 +1,93 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.server; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.UUIDUtil; +import net.minecraft.resources.Identifier; +import net.minecraft.util.datafix.DataFixTypes; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.saveddata.SavedDataType; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Stores Sponge's map ID → UUID index and player UUID table as server-global SavedData. + * Previously stored in {@code SpongeData} within {@code level.dat}. + * + *

Persisted at {@code data/sponge/map_uuids.dat} in the server-level data directory.

+ */ +public final class SpongeMapUUIDData extends SavedData { + + // NBT CompoundTag keys must be strings, so use string-based int codec for map keys + private static final Codec> MAP_INDEX_CODEC = + Codec.unboundedMap(Codec.STRING.xmap(Integer::parseInt, String::valueOf), UUIDUtil.CODEC); + + public static final Codec CODEC = RecordCodecBuilder.create( + i -> i.group( + MAP_INDEX_CODEC.optionalFieldOf("map_uuids", Map.of()).forGetter(SpongeMapUUIDData::mapIndex), + MAP_INDEX_CODEC.optionalFieldOf("player_uuids", Map.of()).forGetter(SpongeMapUUIDData::playerIndex) + ) + .apply(i, SpongeMapUUIDData::new) + ); + + public static final SavedDataType TYPE = new SavedDataType<>( + Identifier.fromNamespaceAndPath("sponge", "map_uuids"), SpongeMapUUIDData::new, CODEC, DataFixTypes.LEVEL + ); + + private final BiMap mapUUIDIndex; + private final BiMap playerUUIDIndex; + + public SpongeMapUUIDData() { + this.mapUUIDIndex = HashBiMap.create(); + this.playerUUIDIndex = HashBiMap.create(); + } + + public SpongeMapUUIDData(final Map mapIndex, final Map playerIndex) { + this.mapUUIDIndex = HashBiMap.create(mapIndex); + this.playerUUIDIndex = HashBiMap.create(playerIndex); + } + + private Map mapIndex() { + return new HashMap<>(this.mapUUIDIndex); + } + + private Map playerIndex() { + return new HashMap<>(this.playerUUIDIndex); + } + + public BiMap mapUUIDIndex() { + return this.mapUUIDIndex; + } + + public BiMap playerUUIDIndex() { + return this.playerUUIDIndex; + } +} diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeRegistryData.java b/src/main/java/org/spongepowered/common/world/server/SpongeRegistryData.java new file mode 100644 index 00000000000..41c789bdd8e --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/server/SpongeRegistryData.java @@ -0,0 +1,99 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.server; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.UUIDUtil; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.util.datafix.DataFixTypes; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.saveddata.SavedDataType; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Optional; +import java.util.UUID; + +/** + * A {@link SavedData} implementation that stores per-dimension Sponge identity, + * consisting of a unique {@link UUID} and a {@link ResourceKey} for the world. + * + *

This is persisted as {@code data/sponge/registry.dat} within each dimension's + * saved data directory.

+ */ +public final class SpongeRegistryData extends SavedData { + + public static final Codec CODEC = RecordCodecBuilder.create( + i -> i.group( + UUIDUtil.CODEC.fieldOf("uuid").forGetter(SpongeRegistryData::uniqueId), + ResourceKey.codec(Registries.DIMENSION).optionalFieldOf("key").forGetter(SpongeRegistryData::resourceKey) + ) + .apply(i, SpongeRegistryData::new) + ); + + public static final SavedDataType TYPE = new SavedDataType<>( + Identifier.fromNamespaceAndPath("sponge", "registry"), SpongeRegistryData::new, CODEC, DataFixTypes.LEVEL + ); + + private UUID uniqueId; + private @Nullable ResourceKey key; + + public SpongeRegistryData() { + this.uniqueId = UUID.randomUUID(); + } + + public SpongeRegistryData(final UUID uniqueId, final Optional> key) { + this.uniqueId = uniqueId; + this.key = key.orElse(null); + } + + public UUID uniqueId() { + return this.uniqueId; + } + + public void setUniqueId(final UUID uniqueId) { + this.uniqueId = uniqueId; + this.setDirty(); + } + + public org.spongepowered.api.@Nullable ResourceKey key() { + if (this.key == null) { + return null; + } + return (org.spongepowered.api.ResourceKey) (Object) this.key.identifier(); + } + + public Optional> resourceKey() { + return Optional.ofNullable(this.key); + } + + public void setKey(final org.spongepowered.api.ResourceKey key) { + this.key = SpongeWorldManager.createRegistryKey(key); + this.setDirty(); + } +} diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeServerLevelData.java b/src/main/java/org/spongepowered/common/world/server/SpongeServerLevelData.java index 8ced7779669..6315915997c 100644 --- a/src/main/java/org/spongepowered/common/world/server/SpongeServerLevelData.java +++ b/src/main/java/org/spongepowered/common/world/server/SpongeServerLevelData.java @@ -24,22 +24,61 @@ */ package org.spongepowered.common.world.server; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.UUIDUtil; +import net.minecraft.core.registries.Registries; +import net.minecraft.server.bossevents.CustomBossEvents; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.levelgen.WorldOptions; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; +import org.spongepowered.common.accessor.server.bossevents.CustomBossEventsAccessor; import org.spongepowered.common.config.inheritable.InheritableConfigHandle; import org.spongepowered.common.config.inheritable.WorldConfig; +import java.util.Optional; import java.util.UUID; public final class SpongeServerLevelData { + + public static final Codec CODEC = RecordCodecBuilder.create( + i -> i.group( + WorldOptions.CODEC.fieldOf("world_gen_options").stable().forGetter(SpongeServerLevelData::worldGenOptions), + CustomBossEventsAccessor.accessor$codec().optionalFieldOf("custom_boss_events").stable().forGetter(SpongeServerLevelData::customBossEvents), + UUIDUtil.CODEC.fieldOf("uuid").stable().forGetter(SpongeServerLevelData::uniqueId), + net.minecraft.resources.ResourceKey.codec(Registries.DIMENSION).optionalFieldOf("key").stable().forGetter(SpongeServerLevelData::resourceKey) + ) + .apply(i, i.stable(SpongeServerLevelData::new)) + ); private @Nullable ResourceKey key; private UUID uniqueId = UUID.randomUUID(); private @Nullable InheritableConfigHandle configAdapter; + private WorldOptions worldGenOptions; + private @Nullable CustomBossEvents customBossEvents; + + public SpongeServerLevelData() { + } + + public SpongeServerLevelData( + WorldOptions worldOptions, Optional customBossEvents, UUID uuid, + Optional> levelResourceKey + ) { + this.worldGenOptions = worldOptions; + this.customBossEvents = customBossEvents.orElse(null); + this.uniqueId = uuid; + this.key = (ResourceKey) levelResourceKey.orElse(null); + } public @Nullable ResourceKey key() { return this.key; } + public Optional> resourceKey() { + return Optional.ofNullable((net.minecraft.resources.ResourceKey) (Object) this.key); + } + public void setKey(final ResourceKey key) { this.key = key; } @@ -52,6 +91,18 @@ public void setUniqueId(final UUID uniqueId) { this.uniqueId = uniqueId; } + public void setWorldGenOptions(final WorldGenSettings worldGenSettings) { + this.worldGenOptions = worldGenSettings.options(); + } + + public void setWorldOptions(final WorldOptions options) { + this.worldGenOptions = options; + } + + public WorldOptions worldGenOptions() { + return this.worldGenOptions; + } + public @Nullable InheritableConfigHandle configAdapter() { return this.configAdapter; } @@ -59,4 +110,13 @@ public void setUniqueId(final UUID uniqueId) { public void setConfigAdapter(final InheritableConfigHandle adapter) { this.configAdapter = adapter; } + + public Optional customBossEvents() { + return Optional.ofNullable(this.customBossEvents); + } + + public void setCustomBossEvents(final CustomBossEvents customBossEvents) { + this.customBossEvents = customBossEvents; + } + } diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeServerWorldPropertiesLoadOptions.java b/src/main/java/org/spongepowered/common/world/server/SpongeServerWorldPropertiesLoadOptions.java new file mode 100644 index 00000000000..4d3e9f8b49c --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/server/SpongeServerWorldPropertiesLoadOptions.java @@ -0,0 +1,164 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.server; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.world.server.WorldArchetype; +import org.spongepowered.api.world.server.storage.ServerWorldProperties; + +import java.util.Optional; +import java.util.function.Consumer; + +public record SpongeServerWorldPropertiesLoadOptions( + @Nullable GetOperation getOperationValue, + @Nullable LoadOperation loadOperationValue, + @Nullable CreateOperation createOperationValue) implements ServerWorldProperties.LoadOptions { + + @Override + public Optional getOperation() { + return Optional.ofNullable(this.getOperationValue); + } + + @Override + public Optional loadOperation() { + return Optional.ofNullable(this.loadOperationValue); + } + + @Override + public Optional createOperation() { + return Optional.ofNullable(this.createOperationValue); + } + + public record GetOperationImpl(@Nullable Consumer callback) implements GetOperation { + + @Override + public Optional> getCallback() { + return Optional.ofNullable(this.callback); + } + } + + public record LoadOperationImpl(@Nullable WorldArchetype overrideWorldArchetypeValue, + @Nullable WorldArchetype fallbackWorldArchetypeValue, + @Nullable Consumer callback) implements LoadOperation { + + @Override + public Optional overrideWorldArchetype() { + return Optional.ofNullable(this.overrideWorldArchetypeValue); + } + + @Override + public Optional fallbackWorldArchetype() { + return Optional.ofNullable(this.fallbackWorldArchetypeValue); + } + + @Override + public Optional> loadCallback() { + return Optional.ofNullable(this.callback); + } + } + + public record CreateOperationImpl(WorldArchetype worldArchetype, @Nullable Consumer callback) implements CreateOperation { + + @Override + public WorldArchetype worldArchetype() { + return this.worldArchetype; + } + + @Override + public Optional> createCallback() { + return Optional.ofNullable(this.callback); + } + } + + public static final class BuilderImpl implements ServerWorldProperties.LoadOptions.Builder.GetStep, + ServerWorldProperties.LoadOptions.Builder.LoadStep, ServerWorldProperties.LoadOptions.Builder.CreateStep { + + private @Nullable GetOperation getOperation; + private @Nullable LoadOperation loadOperation; + private @Nullable CreateOperation createOperation; + + @Override + public GetStep get() { + this.getOperation = new GetOperationImpl(null); + return this; + } + + @Override + public LoadStep load() { + this.loadOperation = new LoadOperationImpl(null, null, null); + return this; + } + + @Override + public CreateStep create(final WorldArchetype worldArchetype) { + this.createOperation = new CreateOperationImpl(worldArchetype, null); + return this; + } + + @Override + public GetStep getCallback(final Consumer getCallback) { + this.getOperation = new GetOperationImpl(getCallback); + return this; + } + + @Override + public LoadStep overrideWorldArchetype(WorldArchetype worldArchetype) { + this.loadOperation = new LoadOperationImpl(worldArchetype, null, this.loadOperation.loadCallback().orElse(null)); + return this; + } + + @Override + public LoadStep fallbackWorldArchetype(WorldArchetype worldArchetype) { + this.loadOperation = new LoadOperationImpl(null, worldArchetype, this.loadOperation.loadCallback().orElse(null)); + return this; + } + + @Override + public LoadStep loadCallback(final Consumer loadCallback) { + this.loadOperation = new LoadOperationImpl(this.loadOperation.overrideWorldArchetype().orElse(null), + this.loadOperation.fallbackWorldArchetype().orElse(null), loadCallback); + return this; + } + + @Override + public CreateStep createCallback(final Consumer initializeCallback) { + this.createOperation = new CreateOperationImpl(this.createOperation.worldArchetype(), initializeCallback); + return this; + } + + @Override + public Builder reset() { + this.getOperation = null; + this.loadOperation = null; + this.createOperation = null; + return this; + } + + @Override + public ServerWorldProperties.LoadOptions build() { + return new SpongeServerWorldPropertiesLoadOptions(this.getOperation, this.loadOperation, this.createOperation); + } + } +} diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeTicketTypeBuilder.java b/src/main/java/org/spongepowered/common/world/server/SpongeTicketTypeBuilder.java index 0fbce2768fd..9a522550451 100644 --- a/src/main/java/org/spongepowered/common/world/server/SpongeTicketTypeBuilder.java +++ b/src/main/java/org/spongepowered/common/world/server/SpongeTicketTypeBuilder.java @@ -28,7 +28,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.util.Ticks; import org.spongepowered.api.world.server.TicketType; -import org.spongepowered.common.accessor.server.level.TicketTypeAccessor; import org.spongepowered.common.util.Constants; import java.util.Comparator; @@ -78,9 +77,10 @@ public TicketType build() { if (this.comparator == null) { this.comparator = (v1, v2) -> 0; } - - return (TicketType) TicketTypeAccessor.accessor$createInstance(this.name, this.comparator, this.lifetime.isInfinite() - ? Constants.ChunkTicket.INFINITE_TIMEOUT - : this.lifetime.ticks()); + final var flag = net.minecraft.server.level.TicketType.FLAG_LOADING + | net.minecraft.server.level.TicketType.FLAG_SIMULATION + | net.minecraft.server.level.TicketType.FLAG_KEEP_DIMENSION_ACTIVE; + final var timeout = this.lifetime.isInfinite() ? Constants.ChunkTicket.INFINITE_TIMEOUT : this.lifetime.ticks(); + return (TicketType) (Object) new net.minecraft.server.level.TicketType(timeout,flag); } } diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeWorldArchetype.java b/src/main/java/org/spongepowered/common/world/server/SpongeWorldArchetype.java new file mode 100644 index 00000000000..6af24a13e5f --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/server/SpongeWorldArchetype.java @@ -0,0 +1,77 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.server; + +import org.spongepowered.api.util.Builder; +import org.spongepowered.api.world.generation.config.WorldGenerationConfig; +import org.spongepowered.api.world.server.WorldArchetype; +import org.spongepowered.api.world.server.WorldArchetypeType; + +import java.util.Objects; +import java.util.Optional; + +public record SpongeWorldArchetype(WorldArchetypeType type, Optional generationConfig) implements WorldArchetype { + + public static final class BuilderImpl implements WorldArchetype.Builder { + + private WorldArchetypeType type; + private WorldGenerationConfig generationConfig; + + public BuilderImpl() { + this.reset(); + } + + @Override + public Builder type(final WorldArchetypeType type) { + this.type = type; + return this; + } + + @Override + public Builder generationConfig(final WorldGenerationConfig generationConfig) { + this.generationConfig = generationConfig; + return this; + } + + @Override + public Builder from(final WorldArchetype value) { + this.type = value.type(); + value.generationConfig().ifPresent(c -> this.generationConfig = c); + return this; + } + + @Override + public Builder reset() { + this.type = null; + this.generationConfig = null; + return this; + } + + @Override + public WorldArchetype build() { + return new SpongeWorldArchetype(Objects.requireNonNull(this.type, "type"), Optional.ofNullable(this.generationConfig)); + } + } +} diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeWorldArchetypeType.java b/src/main/java/org/spongepowered/common/world/server/SpongeWorldArchetypeType.java new file mode 100644 index 00000000000..43ffb9faf22 --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/server/SpongeWorldArchetypeType.java @@ -0,0 +1,77 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.server; + +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import org.spongepowered.api.world.WorldType; +import org.spongepowered.api.world.generation.ChunkGenerator; +import org.spongepowered.api.world.server.WorldArchetypeType; +import org.spongepowered.common.SpongeCommon; + +public final class SpongeWorldArchetypeType implements WorldArchetypeType.Builder { + + private WorldType worldType; + private ChunkGenerator chunkGenerator; + + public SpongeWorldArchetypeType() { + this.reset(); + } + + @Override + public WorldArchetypeType.Builder worldType(final WorldType worldType) { + this.worldType = worldType; + return this; + } + + @Override + public WorldArchetypeType.Builder chunkGenerator(final ChunkGenerator chunkGenerator) { + this.chunkGenerator = chunkGenerator; + return this; + } + + @Override + public WorldArchetypeType.Builder from(final WorldArchetypeType value) { + this.worldType = value.worldType(); + this.chunkGenerator = value.chunkGenerator(); + return this; + } + + @Override + public WorldArchetypeType.Builder reset() { + this.worldType = null; + this.chunkGenerator = null; + return this; + } + + @Override + public WorldArchetypeType build() { + final DimensionType dimensionType = (DimensionType) (Object) this.worldType; + final Registry registry = SpongeCommon.vanillaRegistry(Registries.DIMENSION_TYPE); + return (WorldArchetypeType) (Object) new LevelStem(registry.wrapAsHolder(dimensionType), (net.minecraft.world.level.chunk.ChunkGenerator) this.chunkGenerator); + } +} diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeWorldManager.java b/src/main/java/org/spongepowered/common/world/server/SpongeWorldManager.java index dc5a30cd384..191e1118278 100644 --- a/src/main/java/org/spongepowered/common/world/server/SpongeWorldManager.java +++ b/src/main/java/org/spongepowered/common/world/server/SpongeWorldManager.java @@ -27,42 +27,47 @@ import com.google.common.collect.ImmutableList; import com.mojang.serialization.Dynamic; import com.mojang.serialization.Lifecycle; -import it.unimi.dsi.fastutil.longs.LongIterator; import net.minecraft.CrashReport; import net.minecraft.ReportedException; -import net.minecraft.Util; import net.minecraft.core.BlockPos; +import net.minecraft.core.GlobalPos; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.Registries; import net.minecraft.data.worldgen.features.MiscOverworldFeatures; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.resources.Identifier; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ChunkLoadCounter; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.progress.ChunkProgressListener; -import net.minecraft.util.Mth; -import net.minecraft.util.TimeUtil; -import net.minecraft.world.Difficulty; +import net.minecraft.server.level.progress.LevelLoadListener; +import net.minecraft.server.level.progress.LoggingLevelLoadListener; +import net.minecraft.util.datafix.DataFixers; +import net.minecraft.util.filefix.FileFixException; +import net.minecraft.util.worldupdate.UpgradeProgress; import net.minecraft.world.entity.ai.village.VillageSiege; import net.minecraft.world.entity.npc.CatSpawner; -import net.minecraft.world.entity.npc.WanderingTraderSpawner; -import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.entity.npc.wanderingtrader.WanderingTraderSpawner; import net.minecraft.world.level.CustomSpawner; -import net.minecraft.world.level.ForcedChunksSavedData; -import net.minecraft.world.level.GameRules; -import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelSettings; -import net.minecraft.world.level.WorldDataConfiguration; +import net.minecraft.world.level.TicketStorage; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.dimension.BuiltinDimensionTypes; +import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.DebugLevelSource; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.PatrolSpawner; import net.minecraft.world.level.levelgen.PhantomSpawner; +import net.minecraft.world.level.levelgen.WorldDimensions; +import net.minecraft.world.level.levelgen.WorldGenSettings; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; import net.minecraft.world.level.storage.LevelData; @@ -76,9 +81,6 @@ import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Server; import org.spongepowered.api.Sponge; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPackTypes; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.world.LoadWorldEvent; import org.spongepowered.api.event.world.UnloadWorldEvent; @@ -87,27 +89,26 @@ import org.spongepowered.api.world.DefaultWorldKeys; import org.spongepowered.api.world.WorldType; import org.spongepowered.api.world.server.ServerWorld; +import org.spongepowered.api.world.server.WorldArchetype; +import org.spongepowered.api.world.server.WorldArchetypeType; import org.spongepowered.api.world.server.WorldManager; -import org.spongepowered.api.world.server.WorldTemplate; import org.spongepowered.api.world.server.storage.ServerWorldProperties; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.accessor.server.MinecraftServerAccessor; +import org.spongepowered.common.accessor.world.level.storage.PrimaryLevelDataAccessor; import org.spongepowered.common.bridge.core.MappedRegistryBridge; -import org.spongepowered.common.bridge.server.level.ServerLevelBridge; import org.spongepowered.common.bridge.world.level.chunk.storage.IOWorkerBridge; import org.spongepowered.common.bridge.world.level.dimension.LevelStemBridge; -import org.spongepowered.common.bridge.world.level.storage.LevelStorageAccessBridge; import org.spongepowered.common.bridge.world.level.storage.PrimaryLevelDataBridge; import org.spongepowered.common.bridge.world.level.storage.ServerLevelDataBridge; +import org.spongepowered.common.config.SpongeGameConfigs; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.hooks.PlatformHooks; import org.spongepowered.common.launch.Launch; import org.spongepowered.common.launch.config.core.SpongeConfigs; -import org.spongepowered.common.user.SpongeUserManager; import org.spongepowered.common.util.Constants; import org.spongepowered.common.util.ExecutorUtil; -import java.io.File; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -123,7 +124,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.OptionalLong; import java.util.UUID; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -139,6 +139,7 @@ public class SpongeWorldManager implements WorldManager { private final Path defaultWorldDirectory, customWorldsDirectory; private final Map, ServerLevel> worlds; private final Map, WorldOperationTask> worldOperations = new HashMap<>(); + private final LevelLoadListener levelLoadListener; public SpongeWorldManager(final MinecraftServer server) { this.server = server; @@ -150,6 +151,7 @@ public SpongeWorldManager(final MinecraftServer server) { throw new RuntimeException(e); } this.worlds = ((MinecraftServerAccessor) this.server).accessor$levels(); + this.levelLoadListener = LoggingLevelLoadListener.forDedicatedServer(); } @Override @@ -164,18 +166,13 @@ public Path getDefaultWorldDirectory() { @Override public Optional world(final ResourceKey key) { return Optional.ofNullable((ServerWorld) this.worlds.get(SpongeWorldManager.createRegistryKey(Objects - .requireNonNull(key, "key")))); + .requireNonNull(key, "key")))); } @Override - public Optional worldDirectory(final ResourceKey key) { + public Path worldDirectory(final ResourceKey key) { Objects.requireNonNull(key, "key"); - - Path directory = this.getDirectory(key); - if (Files.notExists(directory)) { - return Optional.empty(); - } - return Optional.of(directory); + return this.getDirectory(key); } @Override @@ -188,26 +185,21 @@ public List worldKeys() { final List worldKeys = new ArrayList<>(); worldKeys.add(DefaultWorldKeys.DEFAULT); - if (Files.exists(this.getDirectory(DefaultWorldKeys.THE_NETHER))) { - worldKeys.add(DefaultWorldKeys.THE_NETHER); - } - - if (Files.exists(this.getDirectory(DefaultWorldKeys.THE_END))) { - worldKeys.add(DefaultWorldKeys.THE_END); - } - + // All dimensions are stored under dimensions/{namespace}/{value}/ try { - for (final Path namespacedDirectory : Files.list(this.customWorldsDirectory).toList()) { - if (this.customWorldsDirectory.equals(namespacedDirectory)) { - continue; - } - - for (final Path valueDirectory : Files.list(namespacedDirectory).toList()) { - if (namespacedDirectory.equals(valueDirectory)) { + if (Files.exists(this.customWorldsDirectory)) { + for (final Path namespacedDirectory : Files.list(this.customWorldsDirectory).toList()) { + if (!Files.isDirectory(namespacedDirectory) || this.customWorldsDirectory.equals(namespacedDirectory)) { continue; } - worldKeys.add(ResourceKey.of(namespacedDirectory.getFileName().toString(), valueDirectory.getFileName().toString())); + for (final Path valueDirectory : Files.list(namespacedDirectory).toList()) { + if (!Files.isDirectory(valueDirectory) || namespacedDirectory.equals(valueDirectory)) { + continue; + } + + worldKeys.add(ResourceKey.of(namespacedDirectory.getFileName().toString(), valueDirectory.getFileName().toString())); + } } } } catch (final IOException e) { @@ -229,7 +221,8 @@ public boolean worldExists(final ResourceKey key) { return true; } - return Files.exists(this.getDirectory(key)); + final Path dimensionDir = this.getDirectory(key); + return Files.exists(dimensionDir); } @Override @@ -252,110 +245,78 @@ public Collection worldsOfType(final WorldType type) { } @Override - public CompletableFuture loadWorld(final WorldTemplate template) { - final ResourceKey key = Objects.requireNonNull(template, "template").key(); - final net.minecraft.resources.ResourceKey registryKey = SpongeWorldManager.createRegistryKey(key); - if (Level.OVERWORLD.equals(registryKey)) { - return CompletableFuture.failedFuture(new IllegalArgumentException("The default world cannot be told to load!")); - } + public CompletableFuture> loadWorld(final ResourceKey key, ServerWorldProperties.LoadOptions propertiesLoadOption) { + final net.minecraft.resources.ResourceKey registryKey = SpongeWorldManager.createRegistryKey(Objects.requireNonNull(key, "key")); return this.performWorldOperation(registryKey, WorldOperationType.LOAD, t -> { - final ServerLevel serverWorld = this.worlds.get(registryKey); - if (serverWorld != null) { - return CompletableFuture.completedFuture((ServerWorld) serverWorld); + if (t.isCancelled()) { + return CompletableFuture.failedFuture(new CancellationException()); } - return this.saveTemplate(template).thenCompose($ -> this.loadWorld0(t, registryKey, ((SpongeWorldTemplate) template).levelStem())); - }); - } - - @Override - public CompletableFuture loadWorld(final ResourceKey key) { - if (DefaultWorldKeys.DEFAULT.equals(key)) { - return CompletableFuture.failedFuture(new IllegalArgumentException("The default world cannot be told to load!")); - } - - final net.minecraft.resources.ResourceKey registryKey = SpongeWorldManager.createRegistryKey(Objects.requireNonNull(key, "key")); - - return this.performWorldOperation(registryKey, WorldOperationType.LOAD, t -> { final ServerLevel world = this.worlds.get(registryKey); if (world != null) { - return CompletableFuture.completedFuture((ServerWorld) world); + final ServerWorldProperties.LoadOptions.@Nullable GetOperation getOperation = propertiesLoadOption.getOperation().orElse(null); + if (getOperation != null) { + getOperation.getCallback().ifPresent(c -> c.accept((ServerWorldProperties) world.getLevelData())); + return CompletableFuture.completedFuture(Optional.of((ServerWorld) world)); + } else if (propertiesLoadOption.loadOperation().isPresent() || propertiesLoadOption.createOperation().isPresent()) { + return CompletableFuture.failedFuture(new IllegalArgumentException("World is already loaded!")); + } } - // First find a loaded level-stem / To load based on a datapack load using the WorldTemplate instead - - final net.minecraft.resources.ResourceKey stemKey = net.minecraft.resources.ResourceKey.create(Registries.LEVEL_STEM, (ResourceLocation) (Object) key); - final @Nullable LevelStem levelStem = SpongeCommon.vanillaRegistry(Registries.LEVEL_STEM).get(stemKey); - if (levelStem != null) { - return this.loadWorld0(t, registryKey, levelStem); + if (Level.OVERWORLD.equals(registryKey) && (propertiesLoadOption.loadOperation().isPresent() || propertiesLoadOption.createOperation().isPresent())) { + return CompletableFuture.failedFuture(new IllegalArgumentException("The default world cannot be told to load!")); } - // Then attempt to load from data pack - final DataPack pack = this.findPack(key); - return this.loadTemplate(pack, key).thenCompose(template -> { - if (template.isEmpty()) { - return CompletableFuture.failedFuture(new IOException(String.format("Failed to load a template for '%s'!", key))); + return SpongeCommon.asyncScheduler().>>submit(() -> { + if (t.isCancelled()) { + return CompletableFuture.failedFuture(new CancellationException()); + } + + final LevelStorageSource.LevelStorageAccess storageSource = this.serverStorageSource(); + + final ServerWorldProperties.LoadOptions.@Nullable LoadOperation loadOperation = propertiesLoadOption.loadOperation().orElse(null); + if (loadOperation != null && this.worldExists(key)) { + final Optional result = this.loadLevelData(storageSource, registryKey, loadOperation); + if (result.isPresent()) { + loadOperation.loadCallback().ifPresent(c -> c.accept((ServerWorldProperties) result.get().data())); + return this.loadWorld0(t, registryKey, result.get(), storageSource); + } + } + + final ServerWorldProperties.LoadOptions.@Nullable CreateOperation createOperation = propertiesLoadOption.createOperation().orElse(null); + if (createOperation != null) { + final LevelDataLoadResult result = this.createLevelData(registryKey, createOperation.worldArchetype()); + createOperation.createCallback().ifPresent(c -> c.accept((ServerWorldProperties) result.data())); + return this.loadWorld0(t, registryKey, result, storageSource); } - return this.loadWorld0(t, registryKey, ((SpongeWorldTemplate) template.get()).levelStem()); - }); + + return CompletableFuture.completedFuture(Optional.empty()); + }).thenCompose(Function.identity()); }); } - private CompletableFuture loadWorld0(final WorldOperationTask task, final net.minecraft.resources.ResourceKey registryKey, final LevelStem levelStem) { + private CompletableFuture> loadWorld0(final WorldOperationTask task, final net.minecraft.resources.ResourceKey registryKey, + final LevelDataLoadResult levelData, final LevelStorageSource.LevelStorageAccess storageSource) { return CompletableFuture.>supplyAsync(() -> { - if (task.isCancelled()) { - return CompletableFuture.failedFuture(new CancellationException()); - } - - final ResourceKey worldKey = (ResourceKey) (Object) registryKey.location(); - - MinecraftServerAccessor.accessor$LOGGER().info("Loading world '{}'", worldKey); + if (task.isCancelled()) { + return CompletableFuture.failedFuture(new CancellationException()); + } - final ServerLevel level; - try { - level = this.createNonDefaultLevel(registryKey, levelStem, worldKey); - } catch (final IOException e) { - return CompletableFuture.failedFuture(new RuntimeException(String.format("Failed to create level data for world '%s'!", worldKey), e)); - } + final ServerLevel world = this.createLevel(registryKey, levelData.stem(), storageSource, levelData.data()); - this.prepareLevel(level); + this.prepareLevel(world); - ((MinecraftServerAccessor) this.server).invoker$forceDifficulty(); + ((MinecraftServerAccessor) this.server).invoker$forceDifficulty(); - return this.loadSpawnChunksAsync(task, level); - }, SpongeCommon.server()) + return this.loadSpawnChunksAsync(task, world); + }, SpongeCommon.server()) .thenCompose(Function.identity()) - .thenApply(w -> (ServerWorld) w); + .thenApply(w -> Optional.of((ServerWorld) w)); } - private LevelStorageSource.LevelStorageAccess createLevelStorageAccess(final ResourceKey worldKey) throws IOException { - LevelStorageSource.LevelStorageAccess storageAccess; - if (this.isVanillaWorld(worldKey)) { - final String directoryName = this.getDirectoryName(worldKey); - storageAccess = LevelStorageSource.createDefault(this.defaultWorldDirectory).createAccess(directoryName); - } else { - final String name = worldKey.namespace() + File.separator + worldKey.value(); - storageAccess = LevelStorageSource.createDefault(this.customWorldsDirectory).createAccess(name); - } - ((LevelStorageAccessBridge) storageAccess).bridge$setDedicated(true); - return storageAccess; - } - - private LevelSettings createLevelSettings(final PrimaryLevelData defaultLevelData, final LevelStem levelStem, final String directoryName) { - final LevelStemBridge levelStemBridge = (LevelStemBridge) (Object) levelStem; - final GameType gameType = levelStemBridge.bridge$gameMode(); - final Boolean hardcore = levelStemBridge.bridge$hardcore(); - final Difficulty difficulty = levelStemBridge.bridge$difficulty(); - final Boolean allowCommands = levelStemBridge.bridge$allowCommands(); - return new LevelSettings( - directoryName, - gameType == null ? defaultLevelData.getGameType() : gameType, - hardcore == null ? defaultLevelData.isHardcore() : hardcore, - difficulty == null ? defaultLevelData.getDifficulty() : difficulty, - allowCommands == null ? defaultLevelData.isAllowCommands() : allowCommands, - defaultLevelData.getGameRules().copy(), - defaultLevelData.getDataConfiguration()); + private LevelStorageSource.LevelStorageAccess serverStorageSource() { + return ((MinecraftServerAccessor) this.server).accessor$storageSource(); } @Override @@ -374,7 +335,6 @@ public CompletableFuture unloadWorld(final ResourceKey key) { return this.unloadWorld((ServerWorld) world); } - @SuppressWarnings("unchecked") @Override public CompletableFuture unloadWorld(final ServerWorld world) { final net.minecraft.resources.ResourceKey registryKey = SpongeWorldManager.createRegistryKey(Objects.requireNonNull(world, "world").key()); @@ -398,46 +358,50 @@ public CompletableFuture unloadWorld(final ServerWorld world) { } @Override - public CompletableFuture> loadProperties(final ResourceKey key) { + public CompletableFuture> loadProperties(final ResourceKey key, ServerWorldProperties.LoadOptions propertiesLoadOptions) { final net.minecraft.resources.ResourceKey registryKey = SpongeWorldManager.createRegistryKey(Objects.requireNonNull(key, "key")); return this.performWorldOperation(registryKey, WorldOperationType.LOAD, t -> { if (t.isCancelled()) { - CompletableFuture.failedFuture(new CancellationException()); - } - - if (this.worlds.get(registryKey) != null) { - return CompletableFuture.completedFuture(Optional.empty()); + return CompletableFuture.failedFuture(new CancellationException()); } - if (!this.worldExists(key)) { - return CompletableFuture.completedFuture(Optional.empty()); + final @Nullable ServerLevel level = this.worlds.get(registryKey); + if (level != null) { + final ServerWorldProperties.LoadOptions.@Nullable GetOperation getOperation = propertiesLoadOptions.getOperation().orElse(null); + if (getOperation != null) { + final ServerWorldProperties properties = (ServerWorldProperties) level.getLevelData(); + getOperation.getCallback().ifPresent(c -> c.accept(properties)); + return CompletableFuture.completedFuture(Optional.of(properties)); + } else if (propertiesLoadOptions.loadOperation().isPresent() || propertiesLoadOptions.createOperation().isPresent()) { + return CompletableFuture.failedFuture(new IllegalArgumentException("World is already loaded!")); + } } return SpongeCommon.asyncScheduler().submit(() -> { if (t.isCancelled()) { - CompletableFuture.failedFuture(new CancellationException()); + throw new CancellationException(); } - final PrimaryLevelData levelData; - try (var storageSource = this.createLevelStorageAccess(key)) { - final PrimaryLevelData defaultLevelData = (PrimaryLevelData) this.server.getWorldData(); - levelData = this.loadLevelData(defaultLevelData.getDataConfiguration(), storageSource.getDataTag()); - } catch (final Exception e) { - throw new RuntimeException(e); + final ServerWorldProperties.LoadOptions.@Nullable LoadOperation loadOperation = propertiesLoadOptions.loadOperation().orElse(null); + if (loadOperation != null && this.worldExists(key)) { + final Optional result = this.loadLevelData(registryKey, loadOperation); + if (result.isPresent()) { + final ServerWorldProperties properties = (ServerWorldProperties) result.get().data(); + loadOperation.loadCallback().ifPresent(c -> c.accept(properties)); + return Optional.of(properties); + } } - final DataPack pack = this.findPack(key); - return this.loadTemplate(pack, key).thenApply(template -> { - if (template.isPresent()) { - final LevelStem scratch = ((SpongeWorldTemplate) template.get()).levelStem(); - ((PrimaryLevelDataBridge) levelData).bridge$populateFromLevelStem(scratch); - } + final ServerWorldProperties.LoadOptions.@Nullable CreateOperation createOperation = propertiesLoadOptions.createOperation().orElse(null); + if (createOperation != null) { + final LevelDataLoadResult result = this.createLevelData(registryKey, createOperation.worldArchetype()); + createOperation.createCallback().ifPresent(c -> c.accept((ServerWorldProperties) result.data())); + return Optional.of((ServerWorldProperties) result.data()); + } - ((PrimaryLevelDataBridge) levelData).bridge$spongeData().setKey(key); - return Optional.of((ServerWorldProperties) levelData); - }); - }).thenCompose(Function.identity()); + return Optional.empty(); + }); }); } @@ -446,39 +410,29 @@ public CompletableFuture saveProperties(final ServerWorldProperties pro final net.minecraft.resources.ResourceKey registryKey = SpongeWorldManager.createRegistryKey(Objects.requireNonNull(properties, "properties").key()); return this.performWorldOperation(registryKey, WorldOperationType.SAVE, t -> { - if (this.worlds.get(registryKey) != null) { - return CompletableFuture.completedFuture(false); + final ServerLevel level = this.worlds.get(registryKey); + if (level != null) { + // World is loaded — save it through the level's normal save mechanism + return SpongeCommon.asyncScheduler().submit(() -> { + level.save(null, true, level.noSave); + return true; + }); } - return SpongeCommon.asyncScheduler().>submit(() -> { + // World is not loaded — save the properties to the level.dat on disk + return SpongeCommon.asyncScheduler().submit(() -> { if (properties instanceof WorldData worldData) { - try { - this.saveLevelDat(worldData, properties.key()); - } catch (Exception ex) { - return CompletableFuture.failedFuture(ex); - } + this.saveLevelDat(worldData, properties.key()); + return true; } - // TODO else: what about DerivedDataLevel? - - // Properties doesn't have everything we need...namely the generator, load the template and set values we actually got - final DataPack pack = this.findPack(properties.key()); - return this.loadTemplate(pack, properties.key()).thenCompose(r -> { - final WorldTemplate template = r.orElse(null); - if (template != null) { - return this.saveTemplate(WorldTemplate.builder().from(template).from(properties).build()); - } - - return CompletableFuture.completedFuture(true); - }); - }).thenCompose(Function.identity()); + return false; + }); }); } - private void saveLevelDat(final WorldData worldData, final ResourceKey key) throws IOException { - try (var storageSource = this.createLevelStorageAccess(key)) { - storageSource.saveDataTag(this.server.registryAccess(), worldData, null); - } + private void saveLevelDat(final WorldData worldData, final ResourceKey key) { + this.serverStorageSource().saveDataTag(worldData); } @Override @@ -511,8 +465,6 @@ public CompletableFuture copyWorld(final ResourceKey key, final Resourc disableLevelSaving = false; } - final boolean isDefaultWorld = DefaultWorldKeys.DEFAULT.equals(key); - return CompletableFuture.runAsync(() -> { final Path originalDirectory = this.getDirectory(key); final Path copyDirectory = this.getDirectory(copyKey); @@ -521,16 +473,6 @@ public CompletableFuture copyWorld(final ResourceKey key, final Resourc Files.walkFileTree(originalDirectory, new SimpleFileVisitor() { @Override public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException { - // Silly recursion if the default world is being copied - if (dir.getFileName().toString().equals(Constants.Sponge.World.DIMENSIONS_DIRECTORY)) { - return FileVisitResult.SKIP_SUBTREE; - } - - // Silly copying of vanilla sub worlds if the default world is being copied - if (isDefaultWorld && SpongeWorldManager.this.isVanillaSubWorld(dir.getFileName().toString())) { - return FileVisitResult.SKIP_SUBTREE; - } - final Path relativize = originalDirectory.relativize(dir); final Path directory = copyDirectory.resolve(relativize); Files.createDirectories(directory); @@ -573,17 +515,15 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr } catch (final IOException e) { throw new CompletionException(e); } - }).whenCompleteAsync(($0, $1) -> { + + final Optional levelData = this.loadLevelData(copyRegistryKey, null); + if (levelData.isPresent()) { + this.saveLevelDat(levelData.get().data(), copyKey); + } + }).thenApplyAsync($ -> { if (loadedWorld != null) { loadedWorld.noSave = disableLevelSaving; } - }, SpongeCommon.server()) - .thenApplyAsync($ -> { - try { - this.server().dataPackManager().copy(this.findPack(key), key, copyKey); - } catch (final IOException e) { - throw new CompletionException(e); - } return true; }, SpongeCommon.server()); @@ -621,7 +561,9 @@ public CompletableFuture moveWorld(final ResourceKey key, final Resourc } private CompletableFuture moveWorld0(final ResourceKey key, final ResourceKey movedKey) { - return CompletableFuture.runAsync(() -> { + final net.minecraft.resources.ResourceKey movedRegistryKey = SpongeWorldManager.createRegistryKey(Objects.requireNonNull(movedKey, "movedKey")); + + return CompletableFuture.supplyAsync(() -> { final Path originalDirectory = this.getDirectory(key); final Path movedDirectory = this.getDirectory(movedKey); @@ -641,15 +583,14 @@ private CompletableFuture moveWorld0(final ResourceKey key, final Resou } catch (final IOException e) { throw new CompletionException(e); } - }).thenApplyAsync($ -> { - try { - this.server().dataPackManager().move(this.findPack(key), key, movedKey); - } catch (final IOException e) { - throw new CompletionException(e); + + final Optional levelData = this.loadLevelData(movedRegistryKey, null); + if (levelData.isPresent()) { + this.saveLevelDat(levelData.get().data(), movedKey); } return true; - }, SpongeCommon.server()); + }); } @Override @@ -668,7 +609,7 @@ public CompletableFuture deleteWorld(final ResourceKey key) { final ServerLevel loadedWorld = this.worlds.get(registryKey); if (loadedWorld != null) { if (!loadedWorld.getPlayers(p -> true).isEmpty()) { - return CompletableFuture.failedFuture(new IOException(String.format("World '%s' was told to unload but players remain.", registryKey.location()))); + return CompletableFuture.failedFuture(new IOException(String.format("World '%s' was told to unload but players remain.", registryKey.identifier()))); } final boolean disableLevelSaving = loadedWorld.noSave; @@ -708,33 +649,23 @@ private CompletableFuture deleteWorld0(final ResourceKey key) { throw new CompletionException(e); } }).thenApplyAsync($ -> { - try { - this.server().dataPackManager().delete(this.findPack(key), key); - } catch (final IOException e) { - throw new CompletionException(e); - } - //After vanilla has detected a new dimension from a data pack it "promotes" it //to the overworld's level data where the level persist even when the data pack is removed. //This forcible removes it from there too. - final Registry levelStemRegistry = SpongeCommon.vanillaRegistry(Registries.LEVEL_STEM); - final net.minecraft.resources.ResourceKey levelStemKey = Registries.levelToLevelStem(registryKey); + final Registry levelStemRegistry = SpongeCommon.server().registryAccess().lookupOrThrow(Registries.LEVEL_STEM); + final net.minecraft.resources.ResourceKey levelStemKey = Registries.levelToLevelStem(registryKey); if (levelStemRegistry.containsKey(levelStemKey)) { ((MappedRegistryBridge) levelStemRegistry).bridge$forceRemoveValue(Registries.levelToLevelStem(registryKey)); } final LevelStorageSource.LevelStorageAccess storageSource = ((MinecraftServerAccessor) this.server).accessor$storageSource(); final PrimaryLevelData levelData = (PrimaryLevelData) this.server.getWorldData(); - storageSource.saveDataTag(SpongeCommon.server().registryAccess(), levelData, null); + storageSource.saveDataTag(levelData); return true; }, SpongeCommon.server()); } - private DataPack findPack(ResourceKey key) { - return this.server().dataPackManager().findPack(DataPackTypes.WORLD, key).orElse(DataPacks.WORLD); - } - private CompletableFuture unloadWorld0(final WorldOperationTask task, final ServerLevel level) { if (task.isCancelled()) { return CompletableFuture.failedFuture(new CancellationException()); @@ -743,7 +674,7 @@ private CompletableFuture unloadWorld0(final WorldOperationTask task, f final net.minecraft.resources.ResourceKey registryKey = level.dimension(); if (!level.getPlayers(p -> true).isEmpty()) { - return CompletableFuture.failedFuture(new IOException(String.format("World '%s' was told to unload but players remain.", registryKey.location()))); + return CompletableFuture.failedFuture(new IOException(String.format("World '%s' was told to unload but players remain.", registryKey.identifier()))); } // We first tell the world to save without flushing @@ -756,10 +687,10 @@ private CompletableFuture unloadWorld0(final WorldOperationTask task, f } if (!level.getPlayers(p -> true).isEmpty()) { - return CompletableFuture.failedFuture(new IOException(String.format("World '%s' was told to unload but players remain.", registryKey.location()))); + return CompletableFuture.failedFuture(new IOException(String.format("World '%s' was told to unload but players remain.", registryKey.identifier()))); } - SpongeCommon.logger().info("Unloading world '{}'", registryKey.location()); + SpongeCommon.logger().info("Unloading world '{}'", registryKey.identifier()); final UnloadWorldEvent unloadWorldEvent = SpongeEventFactory.createUnloadWorldEvent(PhaseTracker.getInstance().currentCause(), (ServerWorld) level); SpongeCommon.post(unloadWorldEvent); @@ -772,7 +703,7 @@ private CompletableFuture unloadWorld0(final WorldOperationTask task, f } while (level.getChunkSource().chunkMap.hasWork()) { - level.getChunkSource().removeTicketsOnClosing(); + level.getChunkSource().deactivateTicketsOnClosing(); final boolean noSave = level.noSave; level.noSave = false; @@ -790,7 +721,7 @@ private CompletableFuture unloadWorld0(final WorldOperationTask task, f try { level.save(null, true, level.noSave); level.close(); - ((ServerLevelBridge) level).bridge$getLevelSave().close(); + // No longer closing per-world storageAccess -- all worlds share the server's single LevelStorageAccess } catch (final Exception ex) { return CompletableFuture.failedFuture(new IOException(ex)); } @@ -802,104 +733,347 @@ private CompletableFuture unloadWorld0(final WorldOperationTask task, f } public void createNonDefaultLevels() { - final Registry registry = SpongeCommon.vanillaRegistry(Registries.LEVEL_STEM); - for (LevelStem levelStem : registry) { + // Migration from old SpongeData format is handled by FileFixerUpperMixin + // which runs BEFORE any ServerLevel is created, preserving UUIDs in + // per-dimension data/sponge/registry.dat files. + // LEVEL_STEM is a dimension registry, loaded separately from worldgen registries. + // It's in the server's composite registryAccess() but NOT in Sponge's scoped holder + // (which explicitly excludes DIMENSION_REGISTRIES during RegistryDataLoader processing). + final Registry registry = PlatformHooks.INSTANCE.getWorldHooks().earlyRegistryAccess(Registries.LEVEL_STEM); + for (final LevelStem levelStem : registry) { final ResourceKey worldKey = (ResourceKey) (Object) registry.getKey(levelStem); if (DefaultWorldKeys.DEFAULT.equals(worldKey)) { continue; } - final LevelStemBridge bridge = (LevelStemBridge) (Object) levelStem; - if (!bridge.bridge$loadOnStartup()) { - SpongeCommon.logger().warn("World '{}' has been disabled from loading at startup. Skipping...", worldKey); + final LevelStemBridge templateBridge = (LevelStemBridge) (Object) levelStem; + if (templateBridge.bridge$hasLegacyData()) { + ((MappedRegistryBridge) registry).bridge$forceRemoveValue(Registries.levelToLevelStem(SpongeWorldManager.createRegistryKey(worldKey))); continue; } - MinecraftServerAccessor.accessor$LOGGER().info("Loading world '{}'", worldKey); final net.minecraft.resources.ResourceKey registryKey = SpongeWorldManager.createRegistryKey(worldKey); + final LevelStorageSource.LevelStorageAccess storageSource = this.serverStorageSource(); try { - final ServerLevel level = this.createNonDefaultLevel(registryKey, levelStem, worldKey); - this.prepareLevel(level); + final LevelDataLoadResult levelData = this.loadLevelData(storageSource, registryKey, null) + .orElseGet(() -> this.createLevelData(registryKey, WorldArchetype.of((WorldArchetypeType) (Object) levelStem))); + if (!((PrimaryLevelDataBridge) levelData.data()).bridge$loadOnStartup()) { + SpongeCommon.logger().warn("World '{}' has been disabled from loading at startup. Skipping...", worldKey); + continue; + } + + final ServerLevel world = this.createLevel(registryKey, levelData.stem(), storageSource, levelData.data()); + this.prepareLevel(world); } catch (final Exception e) { - throw new RuntimeException(String.format("Failed to create level data for world '%s'!", worldKey), e); + throw new IllegalStateException(String.format("Failed to create level data for world '%s'!", worldKey), e); } } } - public void prepareLevels() { - for (final Map.Entry, ServerLevel> entry : this.worlds.entrySet()) { - this.loadSpawnChunks(entry.getValue()); + /** + * Migrates old Sponge world data from previous formats. + * + *

In pre-26.1 Sponge, per-world identity (UUID, key) was stored in a {@code SpongeData} + * compound inside each dimension's {@code level.dat}. In the new format, this data lives + * in {@code data/sponge/registry.dat} per-dimension via {@link SpongeRegistryData}.

+ * + *

This method handles:

+ *
    + *
  • Reading {@code SpongeData} from the root {@code level.dat} (overworld identity)
  • + *
  • Writing it to {@code dimensions/minecraft/overworld/data/sponge/registry.dat}
  • + *
  • Stripping {@code SpongeData} from the root {@code level.dat}
  • + *
+ * + *

Note: Per-dimension {@code level.dat} files for nether/end are lost during vanilla's + * {@code DimensionStorageFileFix} (which deletes {@code DIM-1/} and {@code DIM1/} after + * moving region/entities/poi/data). Those dimensions get fresh UUIDs on first load.

+ */ + private void migrateOldSpongeData() { + final Path rootLevelDat = this.defaultWorldDirectory.resolve("level.dat"); + if (!Files.exists(rootLevelDat)) { + return; } - ((SpongeUserManager) Sponge.server().userManager()).init(); + try { + final CompoundTag root = NbtIo.readCompressed(rootLevelDat, NbtAccounter.defaultQuota()); + final CompoundTag spongeData = root.getCompound("SpongeData").orElse(null); + if (spongeData == null || spongeData.isEmpty()) { + return; // No old SpongeData to migrate + } + + SpongeCommon.logger().info("Migrating old SpongeData from level.dat files to per-dimension SavedData..."); + + // Migrate each dimension that has SpongeData + // Root level.dat = overworld, check dimensions/ for nether/end level.dat + // (vanilla's DimensionStorageFileFix moves region/entities/poi/data from DIM-1/DIM1 + // to dimensions/minecraft/the_nether|the_end but does NOT move level.dat — those are lost. + // However, if someone manually placed them or they survived, we handle them.) + this.migrateOneDimension(spongeData, Level.OVERWORLD, "overworld"); + + // Check for per-dimension level.dat files that may survive in the new directories + for (final var entry : Map.of( + Level.NETHER, "the_nether", + Level.END, "the_end" + ).entrySet()) { + final Path dimLevelDat = DimensionType.getStorageFolder(entry.getKey(), this.defaultWorldDirectory) + .resolve("level.dat"); + if (Files.exists(dimLevelDat)) { + try { + final CompoundTag dimRoot = NbtIo.readCompressed(dimLevelDat, NbtAccounter.defaultQuota()); + final @Nullable CompoundTag dimSpongeData = dimRoot.getCompound("SpongeData").orElse(null); + if (dimSpongeData != null && !dimSpongeData.isEmpty()) { + this.migrateOneDimension(dimSpongeData, entry.getKey(), entry.getValue()); + dimRoot.remove("SpongeData"); + NbtIo.writeCompressed(dimRoot, dimLevelDat); + } + } catch (final Exception e) { + SpongeCommon.logger().warn(" Failed to migrate SpongeData from {}", dimLevelDat, e); + } + } + } + + // Strip SpongeData from root level.dat + root.remove("SpongeData"); + NbtIo.writeCompressed(root, rootLevelDat); + SpongeCommon.logger().info(" Stripped SpongeData from root level.dat"); + + } catch (final Exception e) { + SpongeCommon.logger().warn("Failed to migrate old SpongeData from level.dat — worlds will get fresh UUIDs", e); + } } - private PrimaryLevelData getOrCreateLevelData(@Nullable final Dynamic dynamicLevelData, final LevelStem levelStem, final String directoryName) { - final PrimaryLevelData defaultLevelData = (PrimaryLevelData) this.server.getWorldData(); - if (dynamicLevelData != null) { - try { - return this.loadLevelData(defaultLevelData.getDataConfiguration(), dynamicLevelData); - } catch (Exception e) { - throw new RuntimeException("Failed to load level data from " + directoryName, e); + private void migrateOneDimension( + final CompoundTag spongeData, + final net.minecraft.resources.ResourceKey dimensionKey, + final String dimensionName + ) { + try { + final Path dataDir = DimensionType.getStorageFolder(dimensionKey, this.defaultWorldDirectory) + .resolve("data").resolve("sponge"); + final Path registryFile = dataDir.resolve("registry.dat"); + if (Files.exists(registryFile)) { + return; // Already migrated + } + + final int[] uuidArray = spongeData.getIntArray("UUID").orElse(null); + if (uuidArray == null || uuidArray.length != 4) { + return; } + + final UUID uuid = new UUID( + (long) uuidArray[0] << 32 | uuidArray[1] & 0xFFFFFFFFL, + (long) uuidArray[2] << 32 | uuidArray[3] & 0xFFFFFFFFL + ); + + Files.createDirectories(dataDir); + final SpongeRegistryData registryData = new SpongeRegistryData(uuid, Optional.of(dimensionKey)); + final var encoded = SpongeRegistryData.CODEC.encodeStart(NbtOps.INSTANCE, registryData); + if (encoded.isSuccess()) { + final CompoundTag savedDataTag = new CompoundTag(); + savedDataTag.put("data", encoded.getOrThrow()); + NbtUtils.addCurrentDataVersion(savedDataTag); + NbtIo.writeCompressed(savedDataTag, registryFile); + SpongeCommon.logger().info(" Migrated {} UUID {} to {}", dimensionName, uuid, registryFile); + } + } catch (final Exception e) { + SpongeCommon.logger().warn(" Failed to migrate SpongeData for dimension {}", dimensionName, e); } + } + + private Optional loadLevelData(final net.minecraft.resources.ResourceKey registryKey, + final ServerWorldProperties.LoadOptions.@Nullable LoadOperation loadOperation) { + return this.loadLevelData(this.serverStorageSource(), registryKey, loadOperation); + } + + private Optional loadLevelData( + final LevelStorageSource.LevelStorageAccess storageSource, + final net.minecraft.resources.ResourceKey registryKey, + final ServerWorldProperties.LoadOptions.@Nullable LoadOperation loadOperation + ) { + return Optional.ofNullable(this.loadLevelTag(registryKey)).map(t -> this.readLevelData(storageSource, registryKey, t, loadOperation)); + } - if (this.server.isDemo()) { - return new PrimaryLevelData(MinecraftServer.DEMO_SETTINGS, WorldOptions.DEMO_OPTIONS, SpongeWorldManager.specialWorldProperty(levelStem), Lifecycle.stable()); + private LevelDataLoadResult initializeLevelData(final ResourceKey key, final PrimaryLevelData data, final LevelStem stem) { + final DimensionType dimensionType = stem.type().value(); + final var spongeData = ((PrimaryLevelDataBridge) data).bridge$spongeData(); + spongeData.setKey(key); + spongeData.setConfigAdapter(SpongeGameConfigs.load(dimensionType, key)); + if (spongeData.worldGenOptions() == null) { + spongeData.setWorldGenOptions(this.server.getWorldGenSettings()); } + ((PrimaryLevelDataBridge) data).bridge$populateFromLevelStem(stem); + return new LevelDataLoadResult(data, stem); + } - final LevelSettings levelSettings = this.createLevelSettings(defaultLevelData, levelStem, directoryName); - WorldOptions worldGenOptions = defaultLevelData.worldGenOptions(); - final Long customSeed = ((LevelStemBridge) (Object) levelStem).bridge$seed(); - if (customSeed != null) { - worldGenOptions = worldGenOptions.withSeed(OptionalLong.of(customSeed)); + /** + * Attempts to load a per-dimension level.dat from the dimension's directory. + * With the shared LevelStorageAccess, we cannot use storageSource.getUnfixedDataTag() + * (which reads from the ROOT level.dat). Instead, check the dimension-specific path. + * + *

In the new architecture, non-overworld dimensions do NOT have their own level.dat + * (Sponge no longer writes per-dimension level.dat files). This method returns null + * for dimensions without one, causing loadLevelData to fall through to createLevelData.

+ */ + private @Nullable Dynamic loadLevelTag(final net.minecraft.resources.ResourceKey registryKey) { + final Path dimensionDir = DimensionType.getStorageFolder(registryKey, this.defaultWorldDirectory); + final Path levelDat = dimensionDir.resolve("level.dat"); + if (!Files.exists(levelDat)) { + return null; + } + try { + final CompoundTag root = NbtIo.readCompressed(levelDat, NbtAccounter.defaultQuota()); + return new Dynamic<>(NbtOps.INSTANCE, root.getCompoundOrEmpty("Data")); + } catch (final IOException e) { + SpongeCommon.logger().warn("Failed to load level data from {}", levelDat, e); + return null; } - return new PrimaryLevelData(levelSettings, worldGenOptions, SpongeWorldManager.specialWorldProperty(levelStem), Lifecycle.stable()); } - private PrimaryLevelData loadLevelData(final WorldDataConfiguration datapackConfig, final Dynamic dataTag) { + private LevelDataLoadResult readLevelData( + final LevelStorageSource.LevelStorageAccess worldAccess, + final net.minecraft.resources.ResourceKey registryKey, + final Dynamic dataTag, + final ServerWorldProperties.LoadOptions.@Nullable LoadOperation loadOperation + ) { + // Apply file-fixing and data-fixing before parsing. In 26.1, FileFixerUpper + // requires all level data to be fixed before getLevelDataAndDimensions() can parse it. + // Vanilla handles this for the overworld via the WorldLoader pipeline, but Sponge's + // per-world level.dat files need the same treatment. + final Dynamic fixedDataTag; + try { + fixedDataTag = DataFixers.getFileFixer().fix(worldAccess, dataTag, new UpgradeProgress()); + } catch (final FileFixException e) { + throw new RuntimeException(String.format("Failed to apply data fixes for world '%s'", registryKey.identifier()), e); + } + + final PrimaryLevelData defaultLevelData = (PrimaryLevelData) this.server.getWorldData(); final RegistryAccess.Frozen access = this.server.registryAccess(); - final LevelDataAndDimensions levelData = LevelStorageSource.getLevelDataAndDimensions(dataTag, datapackConfig, access.registryOrThrow(Registries.LEVEL_STEM), access); - return (PrimaryLevelData) levelData.worldData(); + final LevelDataAndDimensions levelData = LevelStorageSource.getLevelDataAndDimensions(worldAccess, + fixedDataTag, defaultLevelData.getDataConfiguration(), access.lookupOrThrow(Registries.LEVEL_STEM), access); + final WorldArchetype worldArchetype = SpongeWorldManager.resolveWorldArchetype(levelData, registryKey, loadOperation); + final LevelStem levelStem = (LevelStem) (Object) worldArchetype.type(); + final PrimaryLevelData worldData = (PrimaryLevelData) levelData.worldDataAndGenSettings().data(); + worldArchetype.generationConfig().ifPresent(o -> + ((PrimaryLevelDataBridge) worldData).bridge$spongeData().setWorldOptions((WorldOptions) o)); + ((PrimaryLevelDataAccessor) worldData).accessor$specialWorldProperty(SpongeWorldManager.specialWorldProperty(levelStem)); + return this.initializeLevelData((ResourceKey) (Object) registryKey.identifier(), worldData, levelStem); } - private ServerLevel createNonDefaultLevel(final net.minecraft.resources.ResourceKey registryKey, final LevelStem levelStem, final ResourceKey worldKey) throws IOException { - if (DefaultWorldKeys.DEFAULT.equals(worldKey)) { - throw new IllegalArgumentException(); + private static WorldArchetype resolveWorldArchetype(final LevelDataAndDimensions levelData, final net.minecraft.resources.ResourceKey registryKey, + final ServerWorldProperties.LoadOptions.@Nullable LoadOperation loadOperation) { + if (loadOperation != null && loadOperation.overrideWorldArchetype().isPresent()) { + return loadOperation.overrideWorldArchetype().get(); + } + + // Worlds that were loaded with Sponge know the associated world archetype. + final Optional spongeWorldKeyStem = Optional.ofNullable(((PrimaryLevelDataBridge) levelData.worldDataAndGenSettings().data()).bridge$spongeData().key()) + .flatMap(worldKey -> levelData.dimensions().dimensions().getOptional( + Registries.levelToLevelStem(SpongeWorldManager.createRegistryKey(worldKey)))); + if (spongeWorldKeyStem.isPresent()) { + return WorldArchetype.of((WorldArchetypeType) (Object) spongeWorldKeyStem.get()); } - final String directoryName = this.getDirectoryName(worldKey); - final LevelStorageSource.LevelStorageAccess storageSource = this.createLevelStorageAccess(worldKey); + // Fallback to best guess. + // TODO: Expose API to iterate the candidates or allow to specify the lookup key? + final Optional worldKeyStem = levelData.dimensions().dimensions().getOptional(Registries.levelToLevelStem(registryKey)); + if (worldKeyStem.isPresent()) { + return WorldArchetype.of((WorldArchetypeType) (Object) worldKeyStem.get()); + } - @Nullable Dynamic dataTag; - try { - dataTag = storageSource.getDataTag(); - } catch (IOException e) { - dataTag = null; + if (loadOperation != null && loadOperation.fallbackWorldArchetype().isPresent()) { + return loadOperation.fallbackWorldArchetype().get(); } - final PrimaryLevelData levelData = this.getOrCreateLevelData(dataTag, levelStem, directoryName); - levelData.setModdedInfo(this.server.getServerModName(), this.server.getModdedStatus().shouldReportAsModified()); + throw new IllegalStateException("The world has no valid WorldArchetype!"); + } + + private LevelDataLoadResult createLevelData(final net.minecraft.resources.ResourceKey registryKey, final WorldArchetype archetype) { + final LevelStem levelStem = (LevelStem) (Object) archetype.type(); + final PrimaryLevelData defaultLevelData = (PrimaryLevelData) this.server.getWorldData(); + final LevelSettings levelSettings = this.createLevelSettings(defaultLevelData, registryKey.identifier().toString()); + final LevelDataLoadResult result = this.initializeLevelData( + (ResourceKey) (Object) registryKey.identifier(), + new PrimaryLevelData(levelSettings, + SpongeWorldManager.specialWorldProperty(levelStem), Lifecycle.stable()), + levelStem + ); + // Apply custom generation config from the archetype (e.g. custom seed) + archetype.generationConfig().ifPresent(o -> + ((PrimaryLevelDataBridge) result.data()).bridge$spongeData().setWorldOptions((WorldOptions) o)); + return result; + } + + private LevelSettings createLevelSettings(final PrimaryLevelData defaultLevelData, final String directoryName) { + return new LevelSettings( + directoryName, + defaultLevelData.getGameType(), + defaultLevelData.getLevelSettings().difficultySettings(), + defaultLevelData.isAllowCommands(), + defaultLevelData.getDataConfiguration() + ); + } + + private ServerLevel createLevel( + final net.minecraft.resources.ResourceKey registryKey, + final LevelStem levelStem, + final LevelStorageSource.LevelStorageAccess storageSource, + final PrimaryLevelData levelData + ) { + + final ResourceKey worldKey = (ResourceKey) (Object) registryKey.identifier(); + final Registry dimensionTypes = this.server.registryAccess().lookupOrThrow(Registries.DIMENSION_TYPE); + final var saveDataSotrage = this.server.getDataStorage(); - ((PrimaryLevelDataBridge) levelData).bridge$populateFromLevelStem(levelStem); - ((PrimaryLevelDataBridge) levelData).bridge$spongeData().setKey(worldKey); + // Resolve inline DimensionType holders to registry references. + // When a LevelStem is deserialized from WorldGenSettings with an inline DimensionType + // (Holder.direct), the login packet encoder fails because STREAM_CODEC expects a registry ID. + // The registry uses IdentityHashMap so wrapAsHolder() won't match deserialized instances. + // Instead, iterate the registry and match by record equals(). + final LevelStem resolvedStem = SpongeWorldManager.resolveInlineDimensionType(levelStem, dimensionTypes); + + MinecraftServerAccessor.accessor$LOGGER().info("Loading world '{}'", worldKey); final List spawners; - if (levelStem.type().is(BuiltinDimensionTypes.OVERWORLD) || levelStem.type().is(BuiltinDimensionTypes.OVERWORLD_CAVES)) { - spawners = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(levelData)); + if (resolvedStem.type().value() == dimensionTypes.getValue(BuiltinDimensionTypes.OVERWORLD) || resolvedStem.type().value() == dimensionTypes.getValue(BuiltinDimensionTypes.OVERWORLD_CAVES)) { + // TODO - 26.1-snapshot-6 figure out if we need to do anything here per world? + spawners = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(saveDataSotrage)); } else { spawners = ImmutableList.of(); } - final long seed = BiomeManager.obfuscateSeed(levelData.worldGenOptions().seed()); + levelData.setModdedInfo(this.server.getServerModName(), this.server.getModdedStatus().shouldReportAsModified()); + final var spongeData = ((PrimaryLevelDataBridge) levelData).bridge$spongeData(); + final var seed = BiomeManager.obfuscateSeed(spongeData.worldGenOptions().seed()); final Executor executor = ((MinecraftServerAccessor) this.server).accessor$executor(); - final ChunkProgressListener progressListener = ((MinecraftServerAccessor) this.server).accessor$progressListenerFactory().create(SpongeWorldManager.getSpawnRadius(levelData)); - final ServerLevel level = new ServerLevel(this.server, executor, storageSource, levelData, registryKey, levelStem, progressListener, levelData.isDebugWorld(), seed, spawners, true, null); - this.worlds.put(registryKey, level); - PlatformHooks.INSTANCE.getWorldHooks().postLoadWorld(level); - return level; + final ServerLevel world = new ServerLevel(this.server, executor, storageSource, levelData, + registryKey, resolvedStem, levelData.isDebugWorld(), seed, spawners, true); + this.worlds.put(registryKey, world); + + // Persist the LevelStem (with resolved holder) in the server's WorldGenSettings so custom + // dimensions survive restart. Using the resolved stem ensures the DimensionType is serialized + // as a registry reference, not inline, breaking the inline cycle for future loads. + final net.minecraft.resources.ResourceKey stemKey = Registries.levelToLevelStem(registryKey); + final WorldGenSettings currentSettings = this.server.getWorldGenSettings(); + if (!currentSettings.dimensions().dimensions().containsKey(stemKey)) { + final Map, LevelStem> mutableDims = new HashMap<>(currentSettings.dimensions().dimensions()); + mutableDims.put(stemKey, resolvedStem); + final WorldGenSettings updated = new WorldGenSettings(currentSettings.options(), new WorldDimensions(mutableDims)); + this.server.getDataStorage().set(WorldGenSettings.TYPE, updated); + } + + // Store per-dimension WorldGenSettings in the dimension's own SavedDataStorage. + // This gives each world its own world_gen_settings.dat with the correct seed/options. + final var levelSpongeData = ((PrimaryLevelDataBridge) levelData).bridge$spongeData(); + final WorldOptions perWorldOptions = levelSpongeData.worldGenOptions() != null + ? levelSpongeData.worldGenOptions() : currentSettings.options(); + final WorldGenSettings perDimSettings = new WorldGenSettings( + perWorldOptions, new WorldDimensions(Map.of(stemKey, resolvedStem))); + world.getDataStorage().set(WorldGenSettings.TYPE, perDimSettings); + + PlatformHooks.INSTANCE.getWorldHooks().postLoadWorld(world); + return world; } private ServerLevel prepareLevel(final ServerLevel level) { @@ -916,30 +1090,29 @@ private ServerLevel prepareLevel(final ServerLevel level) { levelDataBridge.bridge$triggerViewDistanceLogic(); - level.getWorldBorder().applySettings(levelData.getWorldBorder()); - if (!initialized) { if (levelData instanceof WorldData worldData) { try { final boolean isDebugGeneration = worldData.isDebugWorld(); final boolean hasSpawnAlready = levelDataBridge.bridge$customSpawnPosition(); + final var spongeLevelData = levelDataBridge.bridge$spongeData(); if (!hasSpawnAlready) { if (levelDataBridge.bridge$performsSpawnLogic()) { - MinecraftServerAccessor.invoker$setInitialSpawn(level, levelData, worldData.worldGenOptions().generateBonusChest(), isDebugGeneration); + MinecraftServerAccessor.invoker$setInitialSpawn(level, levelData, spongeLevelData.worldGenOptions().generateBonusChest(), isDebugGeneration, server.getLevelLoadListener()); } else if (Level.END.equals(level.dimension())) { - levelData.setSpawn(ServerLevel.END_SPAWN_POINT, 0); + levelData.setSpawn(new LevelData.RespawnData(new GlobalPos(level.dimension(), ServerLevel.END_SPAWN_POINT), 0, 0)); } - } else if (worldData.worldGenOptions().generateBonusChest()) { - final BlockPos pos = levelData.getSpawnPos(); - final ConfiguredFeature bonusChestFeature = SpongeCommon.vanillaRegistry(Registries.CONFIGURED_FEATURE).get(MiscOverworldFeatures.BONUS_CHEST); - bonusChestFeature.place(level, level.getChunkSource().getGenerator(), level.random, pos); + } else if (spongeLevelData.worldGenOptions().generateBonusChest()) { + final BlockPos pos = levelData.getRespawnData().pos(); + final ConfiguredFeature bonusChestFeature = SpongeCommon.vanillaRegistry(Registries.CONFIGURED_FEATURE).getValue(MiscOverworldFeatures.BONUS_CHEST); + bonusChestFeature.place(level, level.getChunkSource().getGenerator(), level.getRandom(), pos); } levelData.setInitialized(true); if (isDebugGeneration) { ((MinecraftServerAccessor) this.server).invoker$setupDebugLevel(worldData); } } catch (final Throwable throwable) { - final CrashReport crashReport = CrashReport.forThrowable(throwable, "Exception initializing world '" + level.dimension().location() + "'"); + final CrashReport crashReport = CrashReport.forThrowable(throwable, "Exception initializing world '" + level.dimension().identifier() + "'"); try { level.fillReportDetails(crashReport); } catch (final Throwable ignore) { @@ -955,8 +1128,10 @@ private ServerLevel prepareLevel(final ServerLevel level) { // Initialize PlayerData in PlayerList, add WorldBorder listener. We change the method in PlayerList to handle per-world border this.server.getPlayerList().addWorldborderListener(level); - if (levelData instanceof WorldData worldData && worldData.getCustomBossEvents() != null) { - ((ServerLevelBridge) level).bridge$getBossBarManager().load(worldData.getCustomBossEvents(), level.registryAccess()); + final var customBossEvents = levelDataBridge.bridge$spongeData().customBossEvents(); + if (levelData instanceof WorldData worldData && customBossEvents != null) { + // TODO - re-add load to bossbarmanager +// ((ServerLevelBridge) level).bridge$getBossBarManager().load(customBossEvents, level.registryAccess()); } return level; @@ -966,13 +1141,18 @@ private ServerLevel prepareLevel(final ServerLevel level) { * Same as loadSpawnChunks but async and without listener. */ private CompletableFuture loadSpawnChunksAsync(final WorldOperationTask operationTask, final ServerLevel level) { - MinecraftServerAccessor.accessor$LOGGER().info("Preparing start region for dimension {}", level.dimension().location()); + MinecraftServerAccessor.accessor$LOGGER().info("Preparing start region for dimension {}", level.dimension().identifier()); - final ServerChunkCache chunkSource = level.getChunkSource(); - level.setDefaultSpawnPos(level.getSharedSpawnPos(), level.getSharedSpawnAngle()); + final var respawnData = level.getRespawnData(); + level.setRespawnData(level.getWorldBorderAdjustedRespawnData(respawnData)); - final int spawnRadius = SpongeWorldManager.getSpawnRadius(level.getLevelData()); - final int spawnSize = spawnRadius > 0 ? Mth.square(ChunkProgressListener.calculateDiameter(spawnRadius)) : 0; + final ChunkLoadCounter chunkLoadCounter = new ChunkLoadCounter(); + chunkLoadCounter.track(level, () -> { + TicketStorage $$1x = level.getDataStorage().get(TicketStorage.TYPE); + if ($$1x != null) { + $$1x.activateAllDeactivatedTickets(); + } + }); final CompletableFuture generationFuture = new CompletableFuture<>(); Sponge.asyncScheduler().submit( @@ -980,128 +1160,44 @@ private CompletableFuture loadSpawnChunksAsync(final WorldOperation if (operationTask.isCancelled()) { generationFuture.cancel(false); task.cancel(); - } else if (chunkSource.getTickingGenerated() >= spawnSize) { + } else if (chunkLoadCounter.pendingChunks() <= 0) { generationFuture.complete(level); // Notify the future that we are done task.cancel(); // And cancel this task - MinecraftServerAccessor.accessor$LOGGER().info("Done preparing start region for dimension {}", level.dimension().location()); + MinecraftServerAccessor.accessor$LOGGER().info("Done preparing start region for dimension {}", level.dimension().identifier()); } }).interval(10, TimeUnit.MILLISECONDS).build() ); - return generationFuture.thenApplyAsync(v -> { - SpongeWorldManager.updateForcedChunks(v, v.getChunkSource()); - return v; - }, SpongeCommon.server()); - } - - /** - * Mimic MinecraftServer#prepareLevels - */ - private void loadSpawnChunks(final ServerLevel level) { - MinecraftServerAccessor.accessor$LOGGER().info("Preparing start region for dimension {}", level.dimension().location()); - - final BlockPos spawnPoint = level.getSharedSpawnPos(); - final ChunkPos chunkPos = new ChunkPos(spawnPoint); - final ChunkProgressListener progressListener = ((ServerLevelBridge) level).bridge$getChunkProgressListener(); - progressListener.updateSpawnPos(chunkPos); - final ServerChunkCache chunkSource = level.getChunkSource(); - ((MinecraftServerAccessor) this.server).accessor$nextTickTimeNanos(Util.getNanos()); - level.setDefaultSpawnPos(spawnPoint, level.getSharedSpawnAngle()); - - final int spawnRadius = SpongeWorldManager.getSpawnRadius(level.getLevelData()); - final int spawnSize = spawnRadius > 0 ? Mth.square(ChunkProgressListener.calculateDiameter(spawnRadius)) : 0; - - while (chunkSource.getTickingGenerated() < spawnSize) { - ((MinecraftServerAccessor) this.server).accessor$nextTickTimeNanos(Util.getNanos() + 10L * TimeUtil.NANOSECONDS_PER_MILLISECOND); - ((MinecraftServerAccessor) this.server).accessor$waitUntilNextTick(); - } - - ((MinecraftServerAccessor) this.server).accessor$nextTickTimeNanos(Util.getNanos() + 10L * TimeUtil.NANOSECONDS_PER_MILLISECOND); - ((MinecraftServerAccessor) this.server).accessor$waitUntilNextTick(); - - SpongeWorldManager.updateForcedChunks(level, chunkSource); - - ((MinecraftServerAccessor) this.server).accessor$nextTickTimeNanos(Util.getNanos() + 10L * TimeUtil.NANOSECONDS_PER_MILLISECOND); - ((MinecraftServerAccessor) this.server).accessor$waitUntilNextTick(); - progressListener.stop(); - } - - private static int getSpawnRadius(final LevelData levelData) { - return ((ServerLevelDataBridge) levelData).bridge$performsSpawnLogic() ? levelData.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS) : 0; - } - - private static void updateForcedChunks(final ServerLevel level, final ServerChunkCache serverChunkProvider) { - final ForcedChunksSavedData forcedChunksSaveData = level.getDataStorage().get(ForcedChunksSavedData.factory(), "chunks"); - if (forcedChunksSaveData != null) { - final LongIterator longIterator = forcedChunksSaveData.getChunks().iterator(); - - while (longIterator.hasNext()) { - final long i = longIterator.nextLong(); - final ChunkPos forceChunkPos = new ChunkPos(i); - serverChunkProvider.updateChunkForced(forceChunkPos, true); - } - } - } - - private CompletableFuture saveTemplate(final WorldTemplate template) { - return this.server().dataPackManager().save(template).thenApply(b -> true); - } - - private CompletableFuture> loadTemplate(final DataPack pack, final ResourceKey key) { - if (this.server().dataPackManager().exists(pack, key)) { - return this.server().dataPackManager().load(pack, key).exceptionally(e -> { - e.printStackTrace(); - return Optional.empty(); - }); - } - return CompletableFuture.completedFuture(Optional.empty()); + return generationFuture; } public static net.minecraft.resources.ResourceKey createRegistryKey(final ResourceKey key) { - return net.minecraft.resources.ResourceKey.create(Registries.DIMENSION, (ResourceLocation) (Object) key); - } - - private String getDirectoryName(final ResourceKey key) { - if (DefaultWorldKeys.DEFAULT.equals(key)) { - return ""; - } - if (DefaultWorldKeys.THE_NETHER.equals(key)) { - return "DIM-1"; - } - if (DefaultWorldKeys.THE_END.equals(key)) { - return "DIM1"; - } - return key.value(); + return net.minecraft.resources.ResourceKey.create(Registries.DIMENSION, (Identifier) (Object) key); } private Path getDirectory(final ResourceKey key) { - if (DefaultWorldKeys.DEFAULT.equals(key)) { - return this.defaultWorldDirectory; - } - if (DefaultWorldKeys.THE_NETHER.equals(key)) { - return this.defaultWorldDirectory.resolve("DIM-1"); - } - if (DefaultWorldKeys.THE_END.equals(key)) { - return this.defaultWorldDirectory.resolve("DIM1"); - } - return this.customWorldsDirectory.resolve(key.namespace()).resolve(key.value()); - } - - private boolean isVanillaWorld(final ResourceKey key) { - return DefaultWorldKeys.DEFAULT.equals(key) || DefaultWorldKeys.THE_NETHER.equals(key) || DefaultWorldKeys.THE_END.equals(key); - } - - private boolean isVanillaSubWorld(final String directoryName) { - return "DIM-1".equals(directoryName) || "DIM1".equals(directoryName); + final net.minecraft.resources.ResourceKey registryKey = SpongeWorldManager.createRegistryKey(key); + return DimensionType.getStorageFolder(registryKey, this.defaultWorldDirectory); } private Path getConfigFile(final ResourceKey key) { return SpongeConfigs.getDirectory().resolve("worlds").resolve(key.namespace()).resolve(key.value() + ".conf"); } + @SuppressWarnings("deprecation") + private static PrimaryLevelData.SpecialWorldProperty specialWorldProperty(final LevelStem stem) { + // Copied from WorldDimensions#specialWorldProperty + final ChunkGenerator generator = stem.generator(); + if (generator instanceof DebugLevelSource) { + return PrimaryLevelData.SpecialWorldProperty.DEBUG; + } else { + return generator instanceof FlatLevelSource ? PrimaryLevelData.SpecialWorldProperty.FLAT : PrimaryLevelData.SpecialWorldProperty.NONE; + } + } + private CompletableFuture performWorldOperation(final net.minecraft.resources.ResourceKey worldKey, final WorldOperationType type, - final Function> function) { + final Function> function) { if (SpongeCommon.server().isStopped()) { return ExecutorUtil.serverManagedBlock(SpongeCommon.server(), function.apply(new WorldOperationTask(type, null))); } @@ -1117,14 +1213,7 @@ public CompletableFuture close() { }).toArray(CompletableFuture[]::new)); } - private static PrimaryLevelData.SpecialWorldProperty specialWorldProperty(final LevelStem stem) { - // Copied from WorldDimensions#specialWorldProperty - final ChunkGenerator generator = stem.generator(); - if (generator instanceof DebugLevelSource) { - return PrimaryLevelData.SpecialWorldProperty.DEBUG; - } else { - return generator instanceof FlatLevelSource ? PrimaryLevelData.SpecialWorldProperty.FLAT : PrimaryLevelData.SpecialWorldProperty.NONE; - } + private record LevelDataLoadResult(PrimaryLevelData data, LevelStem stem) { } private static final class WorldOperationTask { @@ -1181,6 +1270,57 @@ void cancel() { } } + /** + * Resolves inline (Holder.direct) DimensionType holders in a LevelStem to registry references. + * + *

When a LevelStem is deserialized from WorldGenSettings with an inline DimensionType, + * the network STREAM_CODEC (ByteBufCodecs.holderRegistry) can't find the registry ID, + * causing login packet encoding to fail. The DimensionType registry uses IdentityHashMap + * for value lookups, so wrapAsHolder() won't match deserialized record instances. + * Record equals() also fails because HolderSet.Named uses identity comparison.

+ * + *

This method matches by comparing the structural primitive/enum/TagKey fields of + * DimensionType, which are reliably comparable across deserialized instances.

+ */ + static LevelStem resolveInlineDimensionType(final LevelStem stem, final Registry dimensionTypes) { + if (stem.type() instanceof Holder.Reference) { + return stem; // Already a registry reference + } + final DimensionType inline = stem.type().value(); + for (final var entry : dimensionTypes.entrySet()) { + final DimensionType registered = entry.getValue(); + if (SpongeWorldManager.dimensionTypeStructurallyEquals(inline, registered)) { + final Optional> ref = dimensionTypes.get(entry.getKey().identifier()); + if (ref.isPresent()) { + return new LevelStem(ref.get(), stem.generator()); + } + } + } + SpongeCommon.logger().warn("Could not resolve inline DimensionType to registry reference. " + + "Login packets for worlds using this dimension type may fail."); + return stem; + } + + /** + * Compares two DimensionType instances by their structural fields, ignoring Holder/HolderSet + * fields that use identity-based equality (HolderSet.Named, Holder.Reference). + */ + private static boolean dimensionTypeStructurallyEquals(final DimensionType a, final DimensionType b) { + return a.hasFixedTime() == b.hasFixedTime() + && a.hasSkyLight() == b.hasSkyLight() + && a.hasCeiling() == b.hasCeiling() + && a.hasEnderDragonFight() == b.hasEnderDragonFight() + && Double.compare(a.coordinateScale(), b.coordinateScale()) == 0 + && a.minY() == b.minY() + && a.height() == b.height() + && a.logicalHeight() == b.logicalHeight() + && a.infiniburn().equals(b.infiniburn()) + && Float.compare(a.ambientLight(), b.ambientLight()) == 0 + && a.monsterSettings().monsterSpawnBlockLightLimit() == b.monsterSettings().monsterSpawnBlockLightLimit() + && a.skybox() == b.skybox() + && a.cardinalLightType() == b.cardinalLightType(); + } + private enum WorldOperationType { LOAD, UNLOAD, diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeWorldTemplate.java b/src/main/java/org/spongepowered/common/world/server/SpongeWorldTemplate.java index 7afced7969b..a95c4f80967 100644 --- a/src/main/java/org/spongepowered/common/world/server/SpongeWorldTemplate.java +++ b/src/main/java/org/spongepowered/common/world/server/SpongeWorldTemplate.java @@ -24,78 +24,34 @@ */ package org.spongepowered.common.world.server; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import com.mojang.serialization.Codec; -import com.mojang.serialization.JsonOps; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.kyori.adventure.text.Component; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.RegistryOps; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.Difficulty; import net.minecraft.world.level.GameType; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.DataHolder; -import org.spongepowered.api.data.DataManipulator; -import org.spongepowered.api.data.Key; -import org.spongepowered.api.data.Keys; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.data.persistence.Queries; -import org.spongepowered.api.data.value.Value; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.world.SerializationBehavior; -import org.spongepowered.api.world.WorldType; -import org.spongepowered.api.world.WorldTypes; -import org.spongepowered.api.world.biome.provider.BiomeProvider; -import org.spongepowered.api.world.generation.ChunkGenerator; -import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfigs; -import org.spongepowered.api.world.server.ServerWorld; -import org.spongepowered.api.world.server.WorldTemplate; -import org.spongepowered.api.world.server.storage.ServerWorldProperties; -import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.world.level.dimension.LevelStemBridge; -import org.spongepowered.common.bridge.world.level.storage.ServerLevelDataBridge; -import org.spongepowered.common.data.SpongeDataManager; import org.spongepowered.common.data.fixer.SpongeDataCodec; -import org.spongepowered.common.data.holder.SpongeDataHolder; -import org.spongepowered.common.data.provider.DataProviderLookup; import org.spongepowered.common.serialization.EnumCodec; import org.spongepowered.common.serialization.MathCodecs; -import org.spongepowered.common.util.AbstractResourceKeyedBuilder; import org.spongepowered.math.vector.Vector3i; -import java.io.IOException; -import java.util.List; -import java.util.Objects; import java.util.Optional; -public record SpongeWorldTemplate(ResourceKey key, LevelStem levelStem, DataPack pack) implements WorldTemplate, SpongeDataHolder { - - public static final Codec CODEC = RecordCodecBuilder.create( - ($$0) -> $$0.group(DimensionType.CODEC.fieldOf("type").forGetter(LevelStem::type), - net.minecraft.world.level.chunk.ChunkGenerator.CODEC.fieldOf("generator").forGetter(LevelStem::generator)) - .apply($$0, $$0.stable(LevelStem::new))); +public record SpongeWorldTemplate(ResourceKey key, LevelStem levelStem) { private static final Codec SPONGE_CODEC = RecordCodecBuilder .create(r -> r .group( SpongeAdventure.STRING_CODEC.optionalFieldOf("display_name").forGetter(v -> Optional.ofNullable(v.displayName)), - ResourceLocation.CODEC.optionalFieldOf("game_mode").forGetter(v -> Optional.ofNullable(v.gameMode).map(t -> ResourceLocation.fromNamespaceAndPath("sponge", t.getName()))), - ResourceLocation.CODEC.optionalFieldOf("difficulty").forGetter(v -> Optional.ofNullable(v.difficulty).map(t -> ResourceLocation.fromNamespaceAndPath("sponge", t.getKey()))), + Identifier.CODEC.optionalFieldOf("game_mode").forGetter(v -> Optional.ofNullable(v.gameMode).map(t -> Identifier.fromNamespaceAndPath("sponge", t.getName()))), + Identifier.CODEC.optionalFieldOf("difficulty").forGetter(v -> Optional.ofNullable(v.difficulty).map(t -> Identifier.fromNamespaceAndPath("sponge", t.getSerializedName()))), EnumCodec.create(SerializationBehavior.class).optionalFieldOf("serialization_behavior") .forGetter(v -> Optional.ofNullable(v.serializationBehavior)), Codec.INT.optionalFieldOf("view_distance").forGetter(v -> Optional.ofNullable(v.viewDistance)), @@ -118,63 +74,9 @@ public record SpongeWorldTemplate(ResourceKey key, LevelStem levelStem, DataPack ) ); - public static final Codec DIRECT_CODEC = new MapCodec.MapCodecCodec(new SpongeDataCodec<>(LevelStem.CODEC, + public static final Codec DIRECT_CODEC = new MapCodec.MapCodecCodec(new SpongeDataCodec(LevelStem.CODEC, SpongeWorldTemplate.SPONGE_CODEC, (type, data) -> ((LevelStemBridge) (Object) type).bridge$decorateData(data), - type -> ((LevelStemBridge) (Object) type).bridge$createData())); - - @Override - public List impl$delegateDataHolder() { - return List.of((DataHolder) (Object) this.levelStem, this); - } - - public static LevelStem decodeStem(final JsonElement pack, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - SpongeWorldTemplate.fixDimensionDatapack(pack); - return LevelStem.CODEC.parse(ops, pack).getOrThrow(); - } - - public static WorldTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final LevelStem stem = SpongeWorldTemplate.decodeStem(packEntry, registryAccess); - return new SpongeWorldTemplate(key, stem, pack); - } - - public static JsonElement encode(final WorldTemplate s, final RegistryAccess registryAccess) { - if (s instanceof final SpongeWorldTemplate t) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return SpongeWorldTemplate.DIRECT_CODEC.encodeStart(ops, t.levelStem).getOrThrow(); - } - throw new IllegalArgumentException("WorldTemplate is not a SpongeWorldTemplate"); - } - - // TODO datafixer? - // TODO make it automatically work when loading API8 created worlds - private static void fixDimensionDatapack(final JsonElement element) { - try { - final JsonObject biomeSource = element.getAsJsonObject().getAsJsonObject("generator").getAsJsonObject("biome_source"); - if ("minecraft:vanilla_layered".equals(biomeSource.get("type").getAsString())) { - biomeSource.addProperty("type", "minecraft:multi_noise"); - biomeSource.addProperty("preset", "minecraft:overworld"); - } - } catch (final Exception ignored) { - } - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeWorldTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - final DataContainer container = DataFormats.JSON.get().read(serialized.toString()); - container.set(Queries.CONTENT_VERSION, this.contentVersion()); - return container; - } catch (final IOException e) { - throw new IllegalStateException("Could not read deserialized LevelStem:\n" + serialized, e); - } - } + type -> null)); public record SpongeDataSection(@Nullable Component displayName, @Nullable GameType gameMode, @@ -189,160 +91,4 @@ public record SpongeDataSection(@Nullable Component displayName, @Nullable Boolean pvp, @Nullable Long seed) { } - - - public static final class BuilderImpl extends AbstractResourceKeyedBuilder - implements WorldTemplate.Builder { - - private static final DataProviderLookup PROVIDER_LOOKUP = SpongeDataManager.getProviderRegistry().getProviderLookup(LevelStem.class); - - private DataManipulator.Mutable data = DataManipulator.mutableOf(); - private DataPack pack = DataPacks.WORLD; - - @Override - public Builder add(final Key> key, final V value) { - if (!PROVIDER_LOOKUP.getProvider(key).isSupported(LevelStem.class)) { - throw new IllegalArgumentException(key + " is not supported for world templates"); - } - this.data.set(key, value); - return this; - } - - @Override - public Builder reset() { - super.reset(); - this.key = null; - this.data = DataManipulator.mutableOf(); - this.data.set(Keys.WORLD_TYPE, WorldTypes.OVERWORLD.get()); - this.data.set(Keys.CHUNK_GENERATOR, ChunkGenerator.overworld()); - this.pack = DataPacks.WORLD; - return this; - } - - @Override - public Builder from(final WorldTemplate template) { - this.key = Objects.requireNonNull(template, "template").key(); - this.data = DataManipulator.mutableOf(template.getValues()); - this.pack = template.pack(); - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - final LevelStem levelStem = SpongeWorldTemplate.decodeStem(json, SpongeCommon.server().registryAccess()); - return this.from(levelStem); - - } - - @Override - public Builder from(final ServerWorld world) { - this.from(world.properties()); - this.data.set(Keys.CHUNK_GENERATOR, world.generator()); - return this; - } - - @Override - public Builder pack(final DataPack pack) { - this.pack = pack; - return this; - } - - @Override - public Builder from(final ServerWorldProperties properties) { - final ServerLevelDataBridge bridge = (ServerLevelDataBridge) properties; - this.key = properties.key(); - properties.displayName().ifPresent(name -> this.data.set(Keys.DISPLAY_NAME, name)); - this.data.set(Keys.WORLD_TYPE, properties.worldType()); - if (bridge.bridge$customGameType()) { - this.data.set(Keys.GAME_MODE, properties.gameMode()); - } - if (bridge.bridge$customDifficulty()) { - this.data.set(Keys.WORLD_DIFFICULTY, properties.difficulty()); - } - bridge.bridge$serializationBehavior().ifPresent(s -> this.data.set(Keys.SERIALIZATION_BEHAVIOR, s)); - bridge.bridge$viewDistance().ifPresent(v -> this.data.set(Keys.VIEW_DISTANCE, v)); - if (bridge.bridge$customSpawnPosition()) { - this.data.set(Keys.SPAWN_POSITION, properties.spawnPosition()); - } - this.data.set(Keys.IS_LOAD_ON_STARTUP, properties.loadOnStartup()); - this.data.set(Keys.PERFORM_SPAWN_LOGIC, properties.performsSpawnLogic()); - this.data.set(Keys.HARDCORE, properties.hardcore()); - this.data.set(Keys.COMMANDS, properties.commands()); - this.data.set(Keys.PVP, properties.pvp()); - return this; - } - - private Builder from(final LevelStem levelStem) { - this.data.set(((DataHolder) (Object) levelStem).getValues()); - return this; - } - - @Override - protected WorldTemplate build0() { - final ChunkGenerator chunkGenerator = this.data.require(Keys.CHUNK_GENERATOR); - final Holder dimensionType = BuilderImpl.dimensionTypeHolder(this.data.require(Keys.WORLD_TYPE)); - final LevelStem levelStem = new LevelStem(dimensionType, (net.minecraft.world.level.chunk.ChunkGenerator) chunkGenerator); - ((LevelStemBridge) (Object) levelStem).bridge$decorateData(this.data); - return new SpongeWorldTemplate(this.key, levelStem, this.pack); - } - - @NonNull - private static Holder dimensionTypeHolder(final WorldType worldType) { - final Registry dimensionTypeRegistry = SpongeCommon.server().registryAccess().registryOrThrow(Registries.DIMENSION_TYPE); - final ResourceLocation key = dimensionTypeRegistry.getKey((DimensionType) (Object) worldType); - if (key == null) { - return Holder.direct((DimensionType) (Object) worldType); - } - return dimensionTypeRegistry.getHolderOrThrow(net.minecraft.resources.ResourceKey.create(Registries.DIMENSION_TYPE, key)); - } - - } - - public static final class FactoryImpl implements WorldTemplate.Factory { - - @Override - public WorldTemplate overworld() { - return new BuilderImpl() - .reset() - .key(ResourceKey.minecraft("overworld")) - .add(Keys.WORLD_TYPE, WorldTypes.OVERWORLD.get()) - .add(Keys.CHUNK_GENERATOR, ChunkGenerator.overworld()) - .add(Keys.PERFORM_SPAWN_LOGIC, true) - .build(); - } - - @Override - public WorldTemplate overworldCaves() { - return new BuilderImpl() - .reset() - .key(ResourceKey.minecraft("overworld_caves")) - .add(Keys.WORLD_TYPE, WorldTypes.OVERWORLD.get()) - .add(Keys.CHUNK_GENERATOR, ChunkGenerator.noise(BiomeProvider.overworld(), NoiseGeneratorConfigs.CAVES.get())) - .add(Keys.PERFORM_SPAWN_LOGIC, true) - .build(); - } - - @Override - public WorldTemplate theNether() { - return new BuilderImpl() - .reset() - .key(ResourceKey.minecraft("the_nether")) - .add(Keys.WORLD_TYPE, WorldTypes.THE_NETHER.get()) - .add(Keys.CHUNK_GENERATOR, ChunkGenerator.theNether()) - .build(); - } - - @Override - public WorldTemplate theEnd() { - return new BuilderImpl() - .reset() - .key(ResourceKey.minecraft("the_end")) - .add(Keys.WORLD_TYPE, WorldTypes.THE_END.get()) - .add(Keys.CHUNK_GENERATOR, ChunkGenerator.theEnd()) - .build(); - } - - } - } diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeWorldTypeBuilder.java b/src/main/java/org/spongepowered/common/world/server/SpongeWorldTypeBuilder.java new file mode 100644 index 00000000000..a5f5efa8916 --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/server/SpongeWorldTypeBuilder.java @@ -0,0 +1,152 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.server; + +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.registries.Registries; +import net.minecraft.util.valueproviders.UniformInt; +import net.minecraft.world.attribute.BedRule; +import net.minecraft.world.attribute.EnvironmentAttributeMap; +import net.minecraft.world.attribute.EnvironmentAttributes; +import net.minecraft.world.clock.WorldClock; +import net.minecraft.world.clock.WorldClocks; +import net.minecraft.world.level.CardinalLighting; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.dimension.DimensionType; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.data.DataManipulator; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.tag.Tag; +import org.spongepowered.api.util.MinecraftDayTime; +import org.spongepowered.api.util.Range; +import org.spongepowered.api.world.WorldType; +import org.spongepowered.api.world.WorldTypeEffect; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.bridge.tags.TagBridge; +import org.spongepowered.common.bridge.world.level.dimension.DimensionTypeBridge; +import org.spongepowered.common.data.SpongeDataManager; +import org.spongepowered.common.data.provider.DataProviderLookup; + + +public final class SpongeWorldTypeBuilder implements WorldType.Builder { + + private static final DataProviderLookup PROVIDER_LOOKUP = SpongeDataManager.getProviderRegistry().getProviderLookup(DimensionType.class); + + private DataManipulator.Mutable data = DataManipulator.mutableOf(); + + public SpongeWorldTypeBuilder() { + this.reset(); + } + + @Override + public WorldType.Builder add(final Key> key, final V value) { + if (!PROVIDER_LOOKUP.getProvider(key).isSupported(DimensionType.class)) { + throw new IllegalArgumentException(key + " is not supported for world types"); + } + this.data.set(key, value); + return this; + } + + @Override + public WorldType.Builder reset() { + this.data = DataManipulator.mutableOf(); + return this; + } + + @Override + public WorldType.Builder from(final WorldType type) { + this.data.set(type.getValues()); + return this; + } + + @Override + public @NonNull WorldType build() { + @Nullable final WorldTypeEffect effect = this.data.getOrNull(Keys.WORLD_TYPE_EFFECT); + final boolean scorching = this.data.require(Keys.SCORCHING); + final boolean natural = this.data.require(Keys.NATURAL_WORLD_TYPE); + final double coordinateMultiplier = this.data.require(Keys.COORDINATE_MULTIPLIER); + final boolean hasSkylight = this.data.require(Keys.HAS_SKYLIGHT); + final boolean hasCeiling = this.data.require(Keys.HAS_CEILING); + final float ambientLighting = this.data.require(Keys.AMBIENT_LIGHTING); + @Nullable final MinecraftDayTime fixedTime = this.data.getOrNull(Keys.FIXED_TIME); + final boolean bedsUsable = this.data.require(Keys.BEDS_USABLE); + final boolean respawnAnchorsUsable = this.data.require(Keys.RESPAWN_ANCHOR_USABLE); + final int floor = this.data.require(Keys.WORLD_FLOOR); + final int height = this.data.require(Keys.WORLD_HEIGHT); + final int logicalHeight = this.data.require(Keys.WORLD_LOGICAL_HEIGHT); + @Nullable final Tag infiniburn = this.data.getOrNull(Keys.INFINIBURN); + + final boolean piglinSafe = this.data.require(Keys.PIGLIN_SAFE); + final boolean hasRaids = this.data.require(Keys.HAS_RAIDS); + final int monsterSpawnBlockLightLimit = this.data.getOrElse(Keys.SPAWN_LIGHT_LIMIT, 0); + final Range lightRange = this.data.getOrElse(Keys.SPAWN_LIGHT_RANGE, Range.intRange(0, 7)); + final boolean createDragonFight = this.data.getOrElse(Keys.CREATE_DRAGON_FIGHT, false); + final UniformInt monsterSpawnLightTest = UniformInt.of(lightRange.min(), lightRange.max()); + + final SpongeDimensionTypes.SpongeDataSection spongeData = new SpongeDimensionTypes.SpongeDataSection(createDragonFight); + try { + final var clocks = SpongeCommon.vanillaRegistry(Registries.WORLD_CLOCK); + final var blocks = SpongeCommon.vanillaRegistry(Registries.BLOCK); + final HolderSet infiniburnSet = infiniburn == null + ? HolderSet.empty() + : blocks.get(((TagBridge) infiniburn).bridge$asVanillaTag()).map(h -> (HolderSet) h).orElse(HolderSet.empty()); + final var attributes = EnvironmentAttributeMap.builder(); + attributes.set(EnvironmentAttributes.PIGLINS_ZOMBIFY, !piglinSafe); + attributes.set(EnvironmentAttributes.BED_RULE, bedsUsable ? BedRule.CAN_SLEEP_WHEN_DARK : BedRule.EXPLODES); + attributes.set(EnvironmentAttributes.RESPAWN_ANCHOR_WORKS, respawnAnchorsUsable); + attributes.set(EnvironmentAttributes.CAN_START_RAID, hasRaids); + final DimensionType dimensionType = + new DimensionType( + fixedTime != null, + hasSkylight, + hasCeiling, + false, + coordinateMultiplier, + floor, + height, + logicalHeight, + infiniburnSet, + ambientLighting, + new DimensionType.MonsterSettings(monsterSpawnLightTest, monsterSpawnBlockLightLimit), + DimensionType.Skybox.OVERWORLD, + CardinalLighting.Type.DEFAULT, + attributes.build(), + HolderSet.empty(), + clocks.get(WorldClocks.OVERWORLD).map(r -> (Holder) r) + ); + if ((Object) dimensionType instanceof DimensionTypeBridge bridge) { + bridge.bridge$decorateData(spongeData); + } + return (WorldType) (Object) dimensionType; + } catch (IllegalStateException e) { // catch and rethrow minecraft internal exception + throw new IllegalStateException("World type was not valid!", e); + } + } +} diff --git a/src/main/java/org/spongepowered/common/world/server/SpongeWorldTypeTemplate.java b/src/main/java/org/spongepowered/common/world/server/SpongeWorldTypeTemplate.java deleted file mode 100644 index b5ae4af961c..00000000000 --- a/src/main/java/org/spongepowered/common/world/server/SpongeWorldTypeTemplate.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.server; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.Holder; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.RegistryOps; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.util.valueproviders.UniformInt; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.dimension.BuiltinDimensionTypes; -import net.minecraft.world.level.dimension.DimensionType; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.block.BlockType; -import org.spongepowered.api.data.DataManipulator; -import org.spongepowered.api.data.Key; -import org.spongepowered.api.data.Keys; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.data.persistence.Queries; -import org.spongepowered.api.data.value.Value; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.tag.Tag; -import org.spongepowered.api.util.MinecraftDayTime; -import org.spongepowered.api.util.Range; -import org.spongepowered.api.world.WorldType; -import org.spongepowered.api.world.WorldTypeEffect; -import org.spongepowered.api.world.WorldTypeTemplate; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.bridge.world.level.dimension.DimensionTypeBridge; -import org.spongepowered.common.data.SpongeDataManager; -import org.spongepowered.common.data.holder.SpongeDataHolder; -import org.spongepowered.common.data.provider.DataProviderLookup; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.OptionalLong; -import java.util.function.Function; - -public record SpongeWorldTypeTemplate( - ResourceKey key, - DimensionType dimensionType, - DataPack pack -) implements WorldTypeTemplate, SpongeDataHolder { - - public static DimensionType decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return SpongeDimensionTypes.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static WorldTypeTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final DimensionType parsed = SpongeWorldTypeTemplate.decode(packEntry, registryAccess); - return new SpongeWorldTypeTemplate(key, parsed, pack); - } - - public static JsonElement encode(final WorldTypeTemplate template, final RegistryAccess registryAccess) { - if (template instanceof final SpongeWorldTypeTemplate t) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return SpongeDimensionTypes.DIRECT_CODEC.encodeStart(ops, t.dimensionType).getOrThrow(); - } - throw new IllegalArgumentException("WorldTypeTemplate is not a SpongeWorldTypeTemplate"); - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeWorldTypeTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - final DataContainer container = DataFormats.JSON.get().read(serialized.toString()); - container.set(Queries.CONTENT_VERSION, this.contentVersion()); - return container; - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized DimensionType:\n" + serialized, e); - } - } - - public static final class BuilderImpl extends AbstractDataPackEntryBuilder implements WorldTypeTemplate.Builder { - - private static final DataProviderLookup PROVIDER_LOOKUP = SpongeDataManager.getProviderRegistry().getProviderLookup(DimensionType.class); - - private DataManipulator.Mutable data = DataManipulator.mutableOf(); - - public BuilderImpl() { - this.reset(); - } - - @Override - public Function valueExtractor() { - return (w) -> (WorldType) (Object) ((SpongeWorldTypeTemplate) w).dimensionType; - } - - @Override - public WorldTypeTemplate.Builder add(final Key> key, final V value) { - if (!PROVIDER_LOOKUP.getProvider(key).isSupported(DimensionType.class)) { - throw new IllegalArgumentException(key + " is not supported for world types"); - } - this.data.set(key, value); - return this; - } - - @Override - public Builder reset() { - this.data = DataManipulator.mutableOf(); - this.key = null; - this.pack = DataPacks.WORLD_TYPE; - final DimensionType defaultOverworld = SpongeCommon.vanillaRegistry(Registries.DIMENSION_TYPE).get(BuiltinDimensionTypes.OVERWORLD); - this.fromValue((WorldType) (Object) defaultOverworld); - return this; - } - - @Override - public Builder fromValue(final WorldType type) { - this.data.set(type.getValues()); - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - final DataResult> parsed = DimensionType.CODEC.parse(JsonOps.INSTANCE, json); - final DimensionType dimensionType = parsed.getOrThrow().value(); - - this.fromValue((WorldType) (Object) dimensionType); - return this; - } - - @Override - public Builder from(WorldType type) { - this.data.set(type.getValues()); - return this; - } - - @Override - public @NonNull WorldTypeTemplate build0() { - @Nullable final WorldTypeEffect effect = this.data.getOrNull(Keys.WORLD_TYPE_EFFECT); - final boolean scorching = this.data.require(Keys.SCORCHING); - final boolean natural = this.data.require(Keys.NATURAL_WORLD_TYPE); - final double coordinateMultiplier = this.data.require(Keys.COORDINATE_MULTIPLIER); - final boolean hasSkylight = this.data.require(Keys.HAS_SKYLIGHT); - final boolean hasCeiling = this.data.require(Keys.HAS_CEILING); - final float ambientLighting = this.data.require(Keys.AMBIENT_LIGHTING); - @Nullable final MinecraftDayTime fixedTime = this.data.getOrNull(Keys.FIXED_TIME); - final boolean bedsUsable = this.data.require(Keys.BEDS_USABLE); - final boolean respawnAnchorsUsable = this.data.require(Keys.RESPAWN_ANCHOR_USABLE); - final int floor = this.data.require(Keys.WORLD_FLOOR); - final int height = this.data.require(Keys.WORLD_HEIGHT); - final int logicalHeight = this.data.require(Keys.WORLD_LOGICAL_HEIGHT); - @Nullable final Tag infiniburn = this.data.getOrNull(Keys.INFINIBURN); - - final boolean piglinSafe = this.data.require(Keys.PIGLIN_SAFE); - final boolean hasRaids = this.data.require(Keys.HAS_RAIDS); - final int monsterSpawnBlockLightLimit = this.data.getOrElse(Keys.SPAWN_LIGHT_LIMIT, 0); - final Range lightRange = this.data.getOrElse(Keys.SPAWN_LIGHT_RANGE, Range.intRange(0, 7)); - final boolean createDragonFight = this.data.getOrElse(Keys.CREATE_DRAGON_FIGHT, false); - final UniformInt monsterSpawnLightTest = UniformInt.of(lightRange.min(), lightRange.max()); - - final SpongeDimensionTypes.SpongeDataSection spongeData = new SpongeDimensionTypes.SpongeDataSection(createDragonFight); - try { - - final DimensionType dimensionType = - new DimensionType(fixedTime == null ? OptionalLong.empty() : OptionalLong.of(fixedTime.asTicks().ticks()), - hasSkylight, hasCeiling, scorching, natural, coordinateMultiplier, - bedsUsable, respawnAnchorsUsable, - floor, height, logicalHeight, - (TagKey) (Object) infiniburn, - (ResourceLocation) (Object) effect.key(), - ambientLighting, - new DimensionType.MonsterSettings(piglinSafe, hasRaids, monsterSpawnLightTest, monsterSpawnBlockLightLimit)); - if ((Object) dimensionType instanceof DimensionTypeBridge bridge) { - bridge.bridge$decorateData(spongeData); - } - return new SpongeWorldTypeTemplate(this.key, dimensionType, this.pack); - } catch (IllegalStateException e) { // catch and rethrow minecraft internal exception - throw new IllegalStateException(String.format("Template '%s' was not valid!", this.key), e); - } - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/storage/SpongePlayerDataManager.java b/src/main/java/org/spongepowered/common/world/storage/SpongePlayerDataManager.java index 4e23d96b9ee..f3e86cdcbef 100644 --- a/src/main/java/org/spongepowered/common/world/storage/SpongePlayerDataManager.java +++ b/src/main/java/org/spongepowered/common/world/storage/SpongePlayerDataManager.java @@ -28,6 +28,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtIo; +import net.minecraft.world.level.storage.ValueInput; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.Server; import org.spongepowered.api.data.Keys; @@ -52,30 +53,36 @@ public SpongePlayerDataManager(final Server server) { this.playersDirectory = ((SpongeWorldManager) this.server.worldManager()).getDefaultWorldDirectory().resolve("data").resolve(SpongePlayerDataManager.SPONGE_DATA); } - public void readLegacyPlayerData(final ServerPlayer playerEntity, final CompoundTag compound, @Nullable Instant creation) { + public void readLegacyPlayerData(final ServerPlayer playerEntity, final ValueInput compound, @Nullable Instant creation) { if (creation == null) { creation = Instant.now(); } Instant lastPlayed = creation; // first try to migrate bukkit join data stuff - if (compound.contains(Constants.Bukkit.BUKKIT, Constants.NBT.TAG_COMPOUND)) { - final CompoundTag bukkitCompound = compound.getCompound(Constants.Bukkit.BUKKIT); - creation = Instant.ofEpochMilli(bukkitCompound.getLong(Constants.Bukkit.BUKKIT_FIRST_PLAYED)); - lastPlayed = Instant.ofEpochMilli(bukkitCompound.getLong(Constants.Bukkit.BUKKIT_LAST_PLAYED)); + final var bukkit = compound.child(Constants.Bukkit.BUKKIT); + if (bukkit.isPresent()) { + final var bukkitCompound = bukkit.get(); + creation = Instant.ofEpochMilli(bukkitCompound.getLongOr(Constants.Bukkit.BUKKIT_FIRST_PLAYED, 0)); + lastPlayed = Instant.ofEpochMilli(bukkitCompound.getLongOr(Constants.Bukkit.BUKKIT_LAST_PLAYED, 0)); } // migrate canary join data - if (compound.contains(Constants.Canary.ROOT, Constants.NBT.TAG_COMPOUND)) { - final CompoundTag canaryCompound = compound.getCompound(Constants.Canary.ROOT); - creation = Instant.ofEpochMilli(canaryCompound.getLong(Constants.Canary.FIRST_JOINED)); - lastPlayed = Instant.ofEpochMilli(canaryCompound.getLong(Constants.Canary.LAST_JOINED)); + final var canary = compound.child(Constants.Canary.ROOT); + if (canary.isPresent()) { + final var canaryCompound = canary.get(); + creation = Instant.ofEpochMilli(canaryCompound.getLongOr(Constants.Canary.FIRST_JOINED, 0)); + lastPlayed = Instant.ofEpochMilli(canaryCompound.getLongOr(Constants.Canary.LAST_JOINED, 0)); } final Path playerFile = this.playersDirectory.resolve(playerEntity.uniqueId() + ".dat"); if (Files.isReadable(playerFile)) { final CompoundTag playerFileCompound; try (final InputStream stream = Files.newInputStream(playerFile)) { playerFileCompound = NbtIo.readCompressed(stream, NbtAccounter.unlimitedHeap()); - creation = Instant.ofEpochMilli(playerFileCompound.getLong(Constants.Sponge.PlayerData.PLAYER_DATA_JOIN.toString())); - lastPlayed = Instant.ofEpochMilli(playerFileCompound.getLong(Constants.Sponge.PlayerData.PLAYER_DATA_LAST.toString())); + creation = playerFileCompound + .read(Constants.Sponge.PlayerData.PLAYER_DATA_JOIN.toString(), Constants.Sponge.PlayerData.INSTANT_CODEC) + .orElse(Instant.now()); + lastPlayed = playerFileCompound + .read(Constants.Sponge.PlayerData.PLAYER_DATA_LAST.toString(), Constants.Sponge.PlayerData.INSTANT_CODEC) + .orElse(Instant.now()); } catch (final Exception e) { throw new RuntimeException("Failed to decompress playerdata for playerfile " + playerFile, e); } diff --git a/src/main/java/org/spongepowered/common/world/teleport/ConfigTeleportHelperFilter.java b/src/main/java/org/spongepowered/common/world/teleport/ConfigTeleportHelperFilter.java index ec2e0ad212a..c7b4d28208d 100644 --- a/src/main/java/org/spongepowered/common/world/teleport/ConfigTeleportHelperFilter.java +++ b/src/main/java/org/spongepowered/common/world/teleport/ConfigTeleportHelperFilter.java @@ -26,7 +26,7 @@ import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.block.Block; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -66,26 +66,26 @@ private static void updateCacheIfNecessary() { final TeleportHelperCategory teleportHelperCat = SpongeConfigs.getCommon().get().teleportHelper; ConfigTeleportHelperFilter.floorBlockTypes = teleportHelperCat.unsafeFloorBlocks.stream() .map(x -> ResourceKey.resolve(x.toLowerCase(Locale.ENGLISH))) - .map(x -> (BlockType) blockRegistry.get((ResourceLocation) (Object) x)) + .map(x -> (BlockType) blockRegistry.getValue((Identifier) (Object) x)) .filter(Objects::nonNull) .collect(Collectors.toList()); ConfigTeleportHelperFilter.floorBlockStates = teleportHelperCat.unsafeFloorBlocks.stream() .map(x -> ResourceKey.resolve(x.toLowerCase(Locale.ENGLISH))) - .map(x -> blockRegistry.getOptional((ResourceLocation) (Object) x).map(b -> (BlockType) b) + .map(x -> blockRegistry.getOptional((Identifier) (Object) x).map(b -> (BlockType) b) .map(StateContainer::defaultState).orElse(null)) .filter(Objects::nonNull) .collect(Collectors.toList()); ConfigTeleportHelperFilter.bodyBlockTypes = teleportHelperCat.unsafeBlockBlocks.stream() .map(x -> ResourceKey.resolve(x.toLowerCase(Locale.ENGLISH))) - .map(x -> (BlockType) blockRegistry.get((ResourceLocation) (Object) x)) + .map(x -> (BlockType) blockRegistry.getValue((Identifier) (Object) x)) .filter(Objects::nonNull) .collect(Collectors.toList()); ConfigTeleportHelperFilter.bodyBlockStates = teleportHelperCat.unsafeBlockBlocks.stream() .map(x -> ResourceKey.resolve(x.toLowerCase(Locale.ENGLISH))) - .map(x -> blockRegistry.getOptional((ResourceLocation) (Object) x).map(b -> (BlockType) b) + .map(x -> blockRegistry.getOptional((Identifier) (Object) x).map(b -> (BlockType) b) .map(StateContainer::defaultState).orElse(null)) .filter(Objects::nonNull) .collect(Collectors.toList()); diff --git a/src/main/java/org/spongepowered/common/world/teleport/SpongeTeleportHelper.java b/src/main/java/org/spongepowered/common/world/teleport/SpongeTeleportHelper.java index 80746245270..8388ba229da 100644 --- a/src/main/java/org/spongepowered/common/world/teleport/SpongeTeleportHelper.java +++ b/src/main/java/org/spongepowered/common/world/teleport/SpongeTeleportHelper.java @@ -73,12 +73,12 @@ public Optional findSafeLocation(ServerLocation location, int he private Stream getBlockLocations(ServerLocation worldLocation, int height, int width) { // We don't want to warp outside of the world border, so we want to check that we're within it. - final WorldBorder.Settings worldBorder = (WorldBorder.Settings) worldLocation.world().properties().worldBorder(); - final double radius = worldBorder.getSize() / 2.0D; - int worldBorderMinX = GenericMath.floor(worldBorder.getCenterX() - radius); - int worldBorderMinZ = GenericMath.floor(worldBorder.getCenterZ() - radius); - int worldBorderMaxX = GenericMath.floor(worldBorder.getCenterX() + radius); - int worldBorderMaxZ = GenericMath.floor(worldBorder.getCenterZ() + radius); + final WorldBorder.Settings worldBorder = (WorldBorder.Settings) (Object) worldLocation.world().border(); + final double radius = worldBorder.size() / 2.0D; + int worldBorderMinX = GenericMath.floor(worldBorder.centerX() - radius); + int worldBorderMinZ = GenericMath.floor(worldBorder.centerZ() - radius); + int worldBorderMaxX = GenericMath.floor(worldBorder.centerX() + radius); + int worldBorderMaxZ = GenericMath.floor(worldBorder.centerZ() + radius); // Get the World and get the minimum and maximum Y value. int worldMaxY = worldLocation.world().max().y(); diff --git a/src/main/java/org/spongepowered/common/world/volume/VolumeStreamUtils.java b/src/main/java/org/spongepowered/common/world/volume/VolumeStreamUtils.java index af0f0e4a535..da830af2362 100644 --- a/src/main/java/org/spongepowered/common/world/volume/VolumeStreamUtils.java +++ b/src/main/java/org/spongepowered/common/world/volume/VolumeStreamUtils.java @@ -34,13 +34,14 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; -import net.minecraft.util.Tuple; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ImposterProtoChunk; @@ -50,6 +51,8 @@ import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.entity.EntitySection; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.TagValueOutput; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -57,6 +60,7 @@ import org.spongepowered.api.block.entity.BlockEntity; import org.spongepowered.api.block.entity.BlockEntityArchetype; import org.spongepowered.api.entity.EntityArchetype; +import org.spongepowered.api.util.Tuple; import org.spongepowered.api.world.volume.Volume; import org.spongepowered.api.world.volume.game.Region; import org.spongepowered.api.world.volume.stream.StreamOptions; @@ -119,7 +123,7 @@ public static org.spongepowered.api.registry.Registry nativeToSpongeR return (org.spongepowered.api.registry.Registry) registry; } - public static Predicate> entityArchetypePositionFilter(final Vector3i min, final Vector3i max) { + public static Predicate> entityArchetypePositionFilter(final Vector3i min, final Vector3i max) { return VolumeStreamUtils.filterPositions(tuple -> tuple.first().toInt(), min, max); } @@ -145,7 +149,7 @@ public static Predicate filterPositions(final Function pos, final ChunkStatus chunkStatus = shouldGenerate ? ChunkStatus.FULL : ChunkStatus.EMPTY; - final @Nullable ChunkAccess ichunk = readerSupplier.get().getChunk(chunkPos.x, chunkPos.z, chunkStatus, shouldGenerate); + final @Nullable ChunkAccess ichunk = readerSupplier.get().getChunk(chunkPos.x(), chunkPos.z(), chunkStatus, shouldGenerate); if (shouldGenerate) { Objects.requireNonNull(ichunk, "Chunk was expected to load fully and generate, but somehow got a null chunk!"); } @@ -226,14 +230,14 @@ public static void validateStreamArgs(final Vector3i min, final Vector3i max, fi ) { if (chunk.getLevel() instanceof ServerLevel) { return ((PersistentEntitySectionManagerAccessor) ((ServerLevelAccessor) chunk.getLevel()).accessor$getEntityManager()).accessor$sectionStorage() - .getExistingSectionsInChunk(chunk.getPos().toLong()) + .getExistingSectionsInChunk(chunk.getPos().pack()) .flatMap(EntitySection::getEntities) .filter(entity -> VecHelper.inBounds(entity.blockPosition(), min, max)) .map(entity -> new AbstractMap.SimpleEntry<>(entity.blockPosition(), entity)); } else if (Sponge.isClientAvailable() && chunk.getLevel() instanceof ClientLevel) { return ((TransientEntitySectionManagerAccessor) ((ClientLevelAccessor) chunk.getLevel()).accessor$getEntityStorage()) .accessor$sectionStorage() - .getExistingSectionsInChunk(chunk.getPos().toLong()) + .getExistingSectionsInChunk(chunk.getPos().pack()) .flatMap(EntitySection::getEntities) .filter(entity -> VecHelper.inBounds(entity.blockPosition(), min, max)) .map(entity -> new AbstractMap.SimpleEntry<>(entity.blockPosition(), entity)); @@ -248,16 +252,17 @@ public static BiConsumer getOrCloneEnti final Level level ) { return shouldCarbonCopy ? (pos, entity) -> { - final CompoundTag nbt = new CompoundTag(); - entity.save(nbt); - final net.minecraft.world.entity.@Nullable Entity cloned = entity.getType().create(level); + final var output = TagValueOutput.createWithContext(ProblemReporter.DISCARDING, level.registryAccess()); + entity.saveWithoutId(output); + final var input = TagValueInput.create(ProblemReporter.DISCARDING, level.registryAccess(), output.buildResult()); + final net.minecraft.world.entity.@Nullable Entity cloned = entity.getType().create(level, EntitySpawnReason.COMMAND); Objects.requireNonNull( cloned, () -> String.format( "EntityType[%s] creates a null Entity!", - net.minecraft.world.entity.EntityType.getKey(entity.getType()) + EntityType.getKey(entity.getType()) ) - ).load(nbt); + ).load(input); backingVolume.spawnEntity((org.spongepowered.api.entity.Entity) cloned); } : (pos, tile) -> { }; @@ -268,14 +273,16 @@ public static BiConsumer { - final CompoundTag nbt = tile.saveWithFullMetadata(tile.getLevel().registryAccess()); // TODO NPE possible? + final var registryAccess = tile.getLevel().registryAccess(); + final CompoundTag nbt = tile.saveWithFullMetadata(registryAccess); final BlockState state = tile.getBlockState(); final net.minecraft.world.level.block.entity.@Nullable BlockEntity cloned = tile.getType().create(pos, state); + final var input = TagValueInput.create(ProblemReporter.DISCARDING, registryAccess, nbt); Objects.requireNonNull( cloned, () -> String.format( - "TileEntityType[%s] creates a null TileEntity!", BlockEntityType.getKey(tile.getType())) - ).loadWithComponents(nbt, tile.getLevel().registryAccess()); // TODO NPE possible? + "TileEntityType[%s] creates a null TileEntity!", tile.typeHolder().getRegisteredName()) + ).loadWithComponents(input); if (level != null) { ((BlockEntityAccessor) cloned).accessor$level(level); @@ -328,7 +335,7 @@ public static boolean setBiome(final ChunkAccess chunk, final int x, final int y ) { final boolean result = VolumeStreamUtils.setBiome(chunk.getSection(chunk.getSectionIndex(y)), x, y, z, biome); if (result) { - chunk.setUnsaved(true); + chunk.markUnsaved(); } return result; } @@ -379,9 +386,9 @@ private static QuadFunction chunkSectionBlockStateGetter() { return ((chunk, chunkSection, pos) -> chunkSection.getBlockState( - pos.getX() - (chunk.getPos().x << 4), + pos.getX() - (chunk.getPos().x() << 4), pos.getY() & 15, - pos.getZ() - (chunk.getPos().z << 4))); + pos.getZ() - (chunk.getPos().z() << 4))); } private static Function>> getElementByPosition( @@ -395,13 +402,13 @@ private static Function>> getElem return chunk -> { final ChunkPos pos = chunk.getPos(); - final int xStart = pos.x == minCursor.chunkX ? minCursor.xOffset : 0; - final int xEnd = pos.x == maxCursor.chunkX ? maxCursor.xOffset : 15; - final int zStart = pos.z == minCursor.chunkZ ? minCursor.zOffset : 0; - final int zEnd = pos.z == maxCursor.chunkZ ? maxCursor.zOffset : 15; + final int xStart = pos.x() == minCursor.chunkX ? minCursor.xOffset : 0; + final int xEnd = pos.x() == maxCursor.chunkX ? maxCursor.xOffset : 15; + final int zStart = pos.z() == minCursor.chunkZ ? minCursor.zOffset : 0; + final int zEnd = pos.z() == maxCursor.chunkZ ? maxCursor.zOffset : 15; - final int chunkMinX = pos.x << 4; - final int chunkMinZ = pos.z << 4; + final int chunkMinX = pos.x() << 4; + final int chunkMinZ = pos.z() << 4; final LevelChunkSection[] sections = chunk.getSections(); @@ -516,7 +523,7 @@ public static > VolumeStream biomeRegistry = reader.registryAccess().registryOrThrow(Registries.BIOME); + final Registry biomeRegistry = reader.registryAccess().lookupOrThrow(Registries.BIOME); backingVolume = new ObjectArrayMutableBiomeBuffer(min, size, VolumeStreamUtils.nativeToSpongeRegistry(biomeRegistry)); } else { backingVolume = null; @@ -623,8 +630,8 @@ private static SpongeVolumeSt // without consideration, assuming the MC variant is always mixed in to implement the API variant. // Then constructs the VolumeElement final Function, VolumeElement> elementGenerator = (tuple) -> { - final Supplier blockEntitySupplier = VolumeStreamUtils.createWeaklyReferencedSupplier((API) tuple.getB(), "Element"); - final Vector3d blockEntityPos = VecHelper.toVector3d(tuple.getA()); + final Supplier blockEntitySupplier = VolumeStreamUtils.createWeaklyReferencedSupplier((API) tuple.second(), "Element"); + final Vector3d blockEntityPos = VecHelper.toVector3d(tuple.first()); return VolumeElement.of(worldSupplier, blockEntitySupplier, blockEntityPos); }; // Fairly trivial, but just acts as a filter and provides the set of filtered references back to the `poses` @@ -668,7 +675,7 @@ private static SpongeVolumeSt final Stream> volumeStreamBacker = filteredPosStream .map(pos -> filteredPositionEntityAccessor.apply(pos, worldSupplier.get())) .filter(Objects::nonNull) - .filter(tuple -> Objects.nonNull(tuple.getB())) + .filter(tuple -> Objects.nonNull(tuple.first())) .map(elementGenerator); return new SpongeVolumeStream<>(volumeStreamBacker, worldSupplier); } diff --git a/src/main/java/org/spongepowered/common/world/volume/buffer/archetype/SpongeArchetypeVolume.java b/src/main/java/org/spongepowered/common/world/volume/buffer/archetype/SpongeArchetypeVolume.java index 3382370117a..fe838730d56 100644 --- a/src/main/java/org/spongepowered/common/world/volume/buffer/archetype/SpongeArchetypeVolume.java +++ b/src/main/java/org/spongepowered/common/world/volume/buffer/archetype/SpongeArchetypeVolume.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.world.volume.buffer.archetype; +import net.minecraft.server.level.ServerLevel; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.Sponge; import org.spongepowered.api.block.BlockState; @@ -276,7 +277,7 @@ public void applyToWorld( Objects.requireNonNull(target, "Target world cannot be null"); Objects.requireNonNull(placement, "Target position cannot be null"); try (final PhaseContext<@NonNull ?> context = PluginPhase.State.VOLUME_STREAM_APPLICATION - .createPhaseContext(PhaseTracker.SERVER) + .createPhaseContext(PhaseTracker.getWorldInstance((ServerLevel) target)) .spawnType(spawnContext) .source(this)) { context.buildAndSwitch(); diff --git a/src/main/java/org/spongepowered/common/world/weather/SpongeWeather.java b/src/main/java/org/spongepowered/common/world/weather/SpongeWeather.java index a8281fe8437..ce0d59f14f9 100644 --- a/src/main/java/org/spongepowered/common/world/weather/SpongeWeather.java +++ b/src/main/java/org/spongepowered/common/world/weather/SpongeWeather.java @@ -24,7 +24,9 @@ */ package org.spongepowered.common.world.weather; -import net.minecraft.world.level.storage.ServerLevelData; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.saveddata.WeatherData; import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.data.persistence.Queries; import org.spongepowered.api.registry.RegistryTypes; @@ -32,11 +34,14 @@ import org.spongepowered.api.world.weather.Weather; import org.spongepowered.api.world.weather.WeatherType; import org.spongepowered.api.world.weather.WeatherTypes; +import org.spongepowered.common.bridge.world.level.storage.ServerLevelDataBridge; import org.spongepowered.common.util.Constants; import org.spongepowered.common.util.SpongeTicks; public final class SpongeWeather implements Weather { + private static final RandomSource RANDOM = RandomSource.create(); + private final SpongeWeatherType type; private final Ticks remainingDuration, runningDuration; @@ -46,54 +51,55 @@ public SpongeWeather(final SpongeWeatherType type, final Ticks remainingDuration this.runningDuration = runningDuration; } - public static Weather of(final ServerLevelData levelData) { - final boolean thundering = levelData.isThundering(); + public static Weather of(final WeatherData weatherData) { + final boolean thundering = weatherData.isThundering(); if (thundering) { - final Ticks thunderTime = SpongeTicks.ticksOrInfinite(levelData.getThunderTime()); + final Ticks thunderTime = SpongeTicks.ticksOrInfinite(weatherData.getThunderTime()); return new SpongeWeather((SpongeWeatherType) WeatherTypes.THUNDER.get(), thunderTime, thunderTime.isInfinite() ? thunderTime - : new SpongeTicks(6000 - thunderTime.ticks())); + : new SpongeTicks(ServerLevel.THUNDER_DURATION.sample(RANDOM))); } - final boolean raining = levelData.isRaining(); + final boolean raining = weatherData.isRaining(); if (raining) { - final Ticks rainTime = SpongeTicks.ticksOrInfinite(levelData.getRainTime()); + final Ticks rainTime = SpongeTicks.ticksOrInfinite(weatherData.getRainTime()); return new SpongeWeather((SpongeWeatherType) WeatherTypes.RAIN.get(), rainTime, rainTime.isInfinite() ? rainTime - : new SpongeTicks(6000 - rainTime.ticks())); + : new SpongeTicks(ServerLevel.RAIN_DURATION.sample(RANDOM))); } - final Ticks clearWeatherTime = SpongeTicks.ticksOrInfinite(levelData.getClearWeatherTime()); + final Ticks clearWeatherTime = SpongeTicks.ticksOrInfinite(weatherData.getClearWeatherTime()); return new SpongeWeather((SpongeWeatherType) WeatherTypes.CLEAR.get(), clearWeatherTime, clearWeatherTime.isInfinite() ? clearWeatherTime - : new SpongeTicks(6000 - clearWeatherTime.ticks())); + : new SpongeTicks(ServerLevel.RAIN_DURATION.sample(RANDOM) + ServerLevel.THUNDER_DURATION.sample(RANDOM))); } - public static void apply(final ServerLevelData levelData, final Weather weather) { + public static void apply(final ServerLevelDataBridge levelData, final Weather weather) { + final var weatherData = levelData.bridge$level().getWeatherData(); final int time = SpongeTicks.toSaturatedIntOrInfinite(weather.remainingDuration()); final WeatherType type = weather.type(); if (type == WeatherTypes.CLEAR.get()) { - levelData.setClearWeatherTime(time); - levelData.setRaining(false); - levelData.setRainTime(0); - levelData.setThundering(false); - levelData.setThunderTime(0); + weatherData.setClearWeatherTime(time); + weatherData.setRaining(false); + weatherData.setRainTime(0); + weatherData.setThundering(false); + weatherData.setThunderTime(0); } else if (type == WeatherTypes.RAIN.get()) { - levelData.setRaining(true); - levelData.setRainTime(time); - levelData.setThundering(false); - levelData.setThunderTime(0); - levelData.setClearWeatherTime(0); + weatherData.setRaining(true); + weatherData.setRainTime(time); + weatherData.setThundering(false); + weatherData.setThunderTime(0); + weatherData.setClearWeatherTime(0); } else if (type == WeatherTypes.THUNDER.get()) { - levelData.setRaining(true); - levelData.setRainTime(time); - levelData.setThundering(true); - levelData.setThunderTime(time); - levelData.setClearWeatherTime(0); + weatherData.setRaining(true); + weatherData.setRainTime(time); + weatherData.setThundering(true); + weatherData.setThunderTime(time); + weatherData.setClearWeatherTime(0); } } diff --git a/src/main/resources/META-INF/services/org.spongepowered.plugin.PluginLoader b/src/main/resources/META-INF/services/org.spongepowered.plugin.PluginLoader new file mode 100644 index 00000000000..a4e68ba568b --- /dev/null +++ b/src/main/resources/META-INF/services/org.spongepowered.plugin.PluginLoader @@ -0,0 +1 @@ +org.spongepowered.common.launch.plugin.loader.JavaPluginLoader diff --git a/src/main/resources/common.accesswidener b/src/main/resources/common.accesswidener index 77371df3cf0..b1f81e5f02e 100644 --- a/src/main/resources/common.accesswidener +++ b/src/main/resources/common.accesswidener @@ -10,13 +10,16 @@ accessible class net/minecraft/world/entity/animal/TropicalFish$Pattern accessible class net/minecraft/world/entity/raid/Raid$RaidStatus accessible class net/minecraft/server/network/ServerLoginPacketListenerImpl$State extendable method net/minecraft/server/players/IpBanList getIpFromAddress (Ljava/net/SocketAddress;)Ljava/lang/String; -accessible class net/minecraft/server/commands/TeleportCommand$LookAt extendable class net/minecraft/world/item/crafting/Ingredient -extendable method net/minecraft/world/item/crafting/Ingredient (Ljava/util/stream/Stream;)V -accessible field net/minecraft/world/item/crafting/Ingredient values [Lnet/minecraft/world/item/crafting/Ingredient$Value; -accessible class net/minecraft/world/item/crafting/Ingredient$Value -accessible method net/minecraft/world/level/biome/Biome getTemperature (Lnet/minecraft/core/BlockPos;)F +extendable method net/minecraft/world/item/crafting/Ingredient (Lnet/minecraft/core/HolderSet;)V +accessible field net/minecraft/world/item/crafting/Ingredient values Lnet/minecraft/core/HolderSet; +accessible method net/minecraft/world/level/biome/Biome getTemperature (Lnet/minecraft/core/BlockPos;I)F accessible class net/minecraft/network/protocol/game/ServerboundInteractPacket$Action accessible class net/minecraft/network/protocol/game/ServerboundInteractPacket$ActionType accessible class net/minecraft/world/entity/monster/SpellcasterIllager$IllagerSpell accessible class net/minecraft/world/level/biome/Biome$ClimateSettings +accessible class net/minecraft/core/MappedRegistry$TagSet +accessible class net/minecraft/world/level/BaseCommandBlock$CloseableCommandBlockSource +accessible class net/minecraft/server/level/ServerChunkCache$MainThreadExecutor +accessible class net/minecraft/world/entity/monster/illager/SpellcasterIllager$IllagerSpell +accessible class net/minecraft/resources/RegistryDataLoader$RegistryLoadTask \ No newline at end of file diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/event/cause/entity/damage/DamageModifier_BuilderMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/event/cause/entity/damage/DamageModifier_BuilderMixin_ItemStackLike.java deleted file mode 100644 index 651609ec6ac..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/event/cause/entity/damage/DamageModifier_BuilderMixin_ItemStackLike.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.event.cause.entity.damage; - -import org.spongepowered.api.event.cause.entity.damage.DamageModifier; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = DamageModifier.Builder.class, remap = false) -public abstract class DamageModifier_BuilderMixin_ItemStackLike { - - public DamageModifier.Builder item(ItemStack itemStack) { - return ((DamageModifier.Builder) (Object) this).item(itemStack); - } - - public DamageModifier.Builder item(ItemStackSnapshot snapshot) { - return ((DamageModifier.Builder) (Object) this).item(snapshot); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/ArmorEquipableMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/ArmorEquipableMixin_ItemStackLike.java deleted file mode 100644 index 364cfa752b8..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/ArmorEquipableMixin_ItemStackLike.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory; - -import org.spongepowered.api.data.type.HandType; -import org.spongepowered.api.item.inventory.ArmorEquipable; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.function.Supplier; - -@Mixin(value = ArmorEquipable.class, remap = false) -public interface ArmorEquipableMixin_ItemStackLike { - - default void setHead(ItemStack head) { - ((ArmorEquipable) this).setHead(head); - } - - default void setChest(ItemStack chest) { - ((ArmorEquipable) this).setChest(chest); - } - - default void setLegs(ItemStack legs) { - ((ArmorEquipable) this).setLegs(legs); - } - - default void setFeet(ItemStack feet) { - ((ArmorEquipable) this).setFeet(feet); - } - - default void setItemInHand(Supplier handType, ItemStack itemInHand) { - ((ArmorEquipable) this).setItemInHand(handType, itemInHand); - } - - default void setItemInHand(HandType handType, ItemStack itemInHand) { - ((ArmorEquipable) this).setItemInHand(handType, itemInHand); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/EquipableMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/EquipableMixin_ItemStackLike.java deleted file mode 100644 index a1b9f45069e..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/EquipableMixin_ItemStackLike.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory; - -import org.spongepowered.api.item.inventory.Equipable; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.equipment.EquipmentType; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.function.Supplier; - -@Mixin(value = Equipable.class, remap = false) -public interface EquipableMixin_ItemStackLike { - - default boolean canEquip(EquipmentType type, ItemStack equipment) { - return ((Equipable) this).canEquip(type, equipment); - } - - default boolean canEquip(Supplier type, ItemStack equipment) { - return ((Equipable) this).canEquip(type, equipment); - } - - default boolean equip(EquipmentType type, ItemStack equipment) { - return ((Equipable) this).equip(type, equipment); - } - - default boolean equip(Supplier type, ItemStack equipment) { - return ((Equipable) this).equip(type, equipment); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/InventoryMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/InventoryMixin_ItemStackLike.java deleted file mode 100644 index 1c3e2e1d5b3..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/InventoryMixin_ItemStackLike.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory; - -import org.spongepowered.api.item.inventory.Inventory; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.transaction.InventoryTransactionResult; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = Inventory.class, remap = false) -public interface InventoryMixin_ItemStackLike { - - default InventoryTransactionResult offer(ItemStack... stacks) { - return ((Inventory) this).offer(stacks); - } - - default boolean canFit(ItemStack stack) { - return ((Inventory) this).canFit(stack); - } - - default InventoryTransactionResult offer(int index, ItemStack stack) { - return ((Inventory) this).offer(index, stack); - } - - default InventoryTransactionResult set(int index, ItemStack stack) { - return ((Inventory) this).set(index, stack); - } - - default boolean contains(ItemStack stack) { - return ((Inventory) this).contains(stack); - } - - default boolean containsAny(ItemStack stack) { - return ((Inventory) this).containsAny(stack); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/SlotMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/SlotMixin_ItemStackLike.java deleted file mode 100644 index aa7d328d3bb..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/SlotMixin_ItemStackLike.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.Slot; -import org.spongepowered.api.item.inventory.transaction.InventoryTransactionResult; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = Slot.class, remap = false) -public interface SlotMixin_ItemStackLike { - - default InventoryTransactionResult set(ItemStack stack) { - return ((Slot) this).set(stack); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/slot/FilteringSlotMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/slot/FilteringSlotMixin_ItemStackLike.java deleted file mode 100644 index 47ad0ab29f4..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/slot/FilteringSlotMixin_ItemStackLike.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory.slot; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.slot.FilteringSlot; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = FilteringSlot.class, remap = false) -public interface FilteringSlotMixin_ItemStackLike { - - default boolean isValidItem(ItemStack stack) { - return ((FilteringSlot) this).isValidItem(stack); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/transaction/InventoryTransactionResult_BuilderMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/transaction/InventoryTransactionResult_BuilderMixin_ItemStackLike.java deleted file mode 100644 index 51c5d822d0d..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/transaction/InventoryTransactionResult_BuilderMixin_ItemStackLike.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory.transaction; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.item.inventory.transaction.InventoryTransactionResult; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = InventoryTransactionResult.Builder.class, remap = false) -public interface InventoryTransactionResult_BuilderMixin_ItemStackLike { - - default InventoryTransactionResult.Builder reject(ItemStack... itemStacks) { - return ((InventoryTransactionResult.Builder) this).reject(itemStacks); - } - - default InventoryTransactionResult.Builder.PollBuilder poll(ItemStackSnapshot itemStack) { - return ((InventoryTransactionResult.Builder) this).poll(itemStack); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/transaction/SlotTransactionMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/transaction/SlotTransactionMixin_ItemStackLike.java deleted file mode 100644 index 90faa9215ae..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/transaction/SlotTransactionMixin_ItemStackLike.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory.transaction; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.transaction.SlotTransaction; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = SlotTransaction.class, remap = false) -public abstract class SlotTransactionMixin_ItemStackLike { - - public void setCustom(ItemStack stack) { - ((SlotTransaction) (Object) this).setCustom(stack); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/type/GridInventoryMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/type/GridInventoryMixin_ItemStackLike.java deleted file mode 100644 index 54ece4cfa91..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/type/GridInventoryMixin_ItemStackLike.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory.type; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.transaction.InventoryTransactionResult; -import org.spongepowered.api.item.inventory.type.GridInventory; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = GridInventory.class, remap = false) -public interface GridInventoryMixin_ItemStackLike { - - default InventoryTransactionResult set(int x, int y, ItemStack stack) { - return ((GridInventory) this).set(x, y, stack); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/type/Inventory2DMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/type/Inventory2DMixin_ItemStackLike.java deleted file mode 100644 index 58dbb68f0d2..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/type/Inventory2DMixin_ItemStackLike.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory.type; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.transaction.InventoryTransactionResult; -import org.spongepowered.api.item.inventory.type.Inventory2D; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.math.vector.Vector2i; - -@Mixin(value = Inventory2D.class, remap = false) -public interface Inventory2DMixin_ItemStackLike { - - default InventoryTransactionResult set(Vector2i pos, ItemStack stack) { - return ((Inventory2D) this).set(pos, stack); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/merchant/TradeOffer_BuilderMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/merchant/TradeOffer_BuilderMixin_ItemStackLike.java deleted file mode 100644 index 44317b114a9..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/merchant/TradeOffer_BuilderMixin_ItemStackLike.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.merchant; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.merchant.TradeOffer; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = TradeOffer.Builder.class, remap = false) -public interface TradeOffer_BuilderMixin_ItemStackLike { - - default TradeOffer.Builder firstBuyingItem(ItemStack item) { - return ((TradeOffer.Builder) this).firstBuyingItem(item); - } - - default TradeOffer.Builder secondBuyingItem(ItemStack item) { - return ((TradeOffer.Builder) this).secondBuyingItem(item); - } - - default TradeOffer.Builder sellingItem(ItemStack item) { - return ((TradeOffer.Builder) this).sellingItem(item); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/RecipeManagerMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/RecipeManagerMixin_ItemStackLike.java deleted file mode 100644 index 7fa4d9312c2..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/RecipeManagerMixin_ItemStackLike.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.recipe; - -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.item.recipe.Recipe; -import org.spongepowered.api.item.recipe.RecipeManager; -import org.spongepowered.api.item.recipe.RecipeType; -import org.spongepowered.api.item.recipe.cooking.CookingRecipe; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.Collection; -import java.util.Optional; -import java.util.function.Supplier; - -@Mixin(value = RecipeManager.class, remap = false) -public interface RecipeManagerMixin_ItemStackLike { - - default > Collection findByResult(RecipeType type, ItemStackSnapshot result) { - return ((RecipeManager) this).findByResult(type, result); - } - - default > Collection findByResult(Supplier> supplier, ItemStackSnapshot result) { - return ((RecipeManager) this).findByResult(supplier, result); - } - - default Optional findCookingRecipe(RecipeType type, ItemStackSnapshot ingredient) { - return ((RecipeManager) this).findCookingRecipe(type, ingredient); - } - - default Optional findCookingRecipe(Supplier> supplier, ItemStackSnapshot ingredient) { - return ((RecipeManager) this).findCookingRecipe(supplier, ingredient); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/cooking/CookingRecipe_Builder_ResultStepMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/cooking/CookingRecipe_Builder_ResultStepMixin_ItemStackLike.java deleted file mode 100644 index 2a214b60a64..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/cooking/CookingRecipe_Builder_ResultStepMixin_ItemStackLike.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.recipe.cooking; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.item.recipe.cooking.CookingRecipe; -import org.spongepowered.api.item.recipe.crafting.RecipeInput; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.function.Function; - -@Mixin(value = CookingRecipe.Builder.ResultStep.class, remap = false) -public interface CookingRecipe_Builder_ResultStepMixin_ItemStackLike { - - default CookingRecipe.Builder.EndStep result(ItemStack result) { - return ((CookingRecipe.Builder.ResultStep) this).result(result); - } - - default CookingRecipe.Builder.EndStep result(ItemStackSnapshot result) { - return ((CookingRecipe.Builder.ResultStep) this).result(result); - } - - default CookingRecipe.Builder.EndStep result(Function resultFunction, ItemStack exemplaryResult) { - return ((CookingRecipe.Builder.ResultStep) this).result(resultFunction, exemplaryResult); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/IngredientMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/IngredientMixin_ItemStackLike.java deleted file mode 100644 index 9fa21b80a57..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/IngredientMixin_ItemStackLike.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.recipe.crafting; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.recipe.crafting.Ingredient; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = Ingredient.class, remap = false) -public interface IngredientMixin_ItemStackLike { - - default boolean test(ItemStack itemStack) { - return ((Ingredient) this).test(itemStack); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/Ingredient_BuilderMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/Ingredient_BuilderMixin_ItemStackLike.java deleted file mode 100644 index 459f8203986..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/Ingredient_BuilderMixin_ItemStackLike.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.recipe.crafting; - -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.item.recipe.crafting.Ingredient; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.function.Predicate; - -@Mixin(value = Ingredient.Builder.class, remap = false) -public interface Ingredient_BuilderMixin_ItemStackLike { - - default Ingredient.Builder with(ItemStack... types) { - return ((Ingredient.Builder) this).with(types); - } - - default Ingredient.Builder with(ResourceKey resourceKey, Predicate predicate, ItemStack... exemplaryTypes) { - return ((Ingredient.Builder) this).with(resourceKey, itemStack -> predicate.test(itemStack.asMutable()), exemplaryTypes); - } - - default Ingredient.Builder with(ItemStackSnapshot... types) { - return ((Ingredient.Builder) this).with(types); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/RecipeInput_FactoryMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/RecipeInput_FactoryMixin_ItemStackLike.java deleted file mode 100644 index e5bf505f383..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/RecipeInput_FactoryMixin_ItemStackLike.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.recipe.crafting; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.recipe.crafting.RecipeInput; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = RecipeInput.Factory.class, remap = false) -public interface RecipeInput_FactoryMixin_ItemStackLike { - - default RecipeInput.Single single(ItemStack stack) { - return ((RecipeInput.Factory) this).single(stack); - } - - default RecipeInput.Smithing smithing(ItemStack templateStack, ItemStack baseStack, ItemStack additionStack) { - return ((RecipeInput.Factory) this).smithing(templateStack, baseStack, additionStack); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/ShapedCraftingRecipe_Builder_ResultStepMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/ShapedCraftingRecipe_Builder_ResultStepMixin_ItemStackLike.java deleted file mode 100644 index d02c9ae6dd1..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/ShapedCraftingRecipe_Builder_ResultStepMixin_ItemStackLike.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.recipe.crafting; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.item.recipe.crafting.RecipeInput; -import org.spongepowered.api.item.recipe.crafting.ShapedCraftingRecipe; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.function.Function; - -@Mixin(value = ShapedCraftingRecipe.Builder.ResultStep.class, remap = false) -public interface ShapedCraftingRecipe_Builder_ResultStepMixin_ItemStackLike { - - default ShapedCraftingRecipe.Builder.EndStep result(ItemStackSnapshot result) { - return ((ShapedCraftingRecipe.Builder.ResultStep) this).result(result); - } - - default ShapedCraftingRecipe.Builder.EndStep result(ItemStack result) { - return ((ShapedCraftingRecipe.Builder.ResultStep) this).result(result); - } - - default ShapedCraftingRecipe.Builder.EndStep result(Function resultFunction, ItemStack exemplaryResult) { - return ((ShapedCraftingRecipe.Builder.ResultStep) this).result(resultFunction, exemplaryResult); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/ShapelessCraftingRecipe_Builder_ResultStepMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/ShapelessCraftingRecipe_Builder_ResultStepMixin_ItemStackLike.java deleted file mode 100644 index c0cee571a57..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/ShapelessCraftingRecipe_Builder_ResultStepMixin_ItemStackLike.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.recipe.crafting; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.item.recipe.crafting.RecipeInput; -import org.spongepowered.api.item.recipe.crafting.ShapelessCraftingRecipe; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.function.Function; - -@Mixin(value = ShapelessCraftingRecipe.Builder.ResultStep.class, remap = false) -public interface ShapelessCraftingRecipe_Builder_ResultStepMixin_ItemStackLike { - - default ShapelessCraftingRecipe.Builder.EndStep result(ItemStackSnapshot result) { - return ((ShapelessCraftingRecipe.Builder.ResultStep) this).result(result); - } - - default ShapelessCraftingRecipe.Builder.EndStep result(ItemStack result) { - return ((ShapelessCraftingRecipe.Builder.ResultStep) this).result(result); - } - - default ShapelessCraftingRecipe.Builder.EndStep result(Function resultFunction, ItemStack exemplaryResult) { - return ((ShapelessCraftingRecipe.Builder.ResultStep) this).result(resultFunction, exemplaryResult); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/SpecialCraftingRecipe_Builder_ResultStepMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/SpecialCraftingRecipe_Builder_ResultStepMixin_ItemStackLike.java deleted file mode 100644 index 9678264a3c2..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/crafting/SpecialCraftingRecipe_Builder_ResultStepMixin_ItemStackLike.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.recipe.crafting; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.recipe.crafting.SpecialCraftingRecipe; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = SpecialCraftingRecipe.Builder.ResultStep.class, remap = false) -public interface SpecialCraftingRecipe_Builder_ResultStepMixin_ItemStackLike { - - default SpecialCraftingRecipe.Builder.EndStep result(ItemStack result) { - return ((SpecialCraftingRecipe.Builder.ResultStep) this).result(result); - } - - -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/single/StoneCutterRecipe_Builder_ResultStepMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/single/StoneCutterRecipe_Builder_ResultStepMixin_ItemStackLike.java deleted file mode 100644 index 00ff09262bd..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/single/StoneCutterRecipe_Builder_ResultStepMixin_ItemStackLike.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.recipe.single; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.item.recipe.crafting.RecipeInput; -import org.spongepowered.api.item.recipe.single.StoneCutterRecipe; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.function.Function; - -@Mixin(value = StoneCutterRecipe.Builder.ResultStep.class, remap = false) -public interface StoneCutterRecipe_Builder_ResultStepMixin_ItemStackLike { - - default StoneCutterRecipe.Builder.EndStep result(ItemStackSnapshot result) { - return ((StoneCutterRecipe.Builder.ResultStep) this).result(result); - } - - default StoneCutterRecipe.Builder.EndStep result(ItemStack result) { - return ((StoneCutterRecipe.Builder.ResultStep) this).result(result); - } - - default StoneCutterRecipe.Builder.EndStep result(Function resultFunction, ItemStack exemplaryResult) { - return ((StoneCutterRecipe.Builder.ResultStep) this).result(resultFunction, exemplaryResult); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/smithing/SmithingRecipe_Builder_ResultStepMixin_ItemStackLike.java b/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/smithing/SmithingRecipe_Builder_ResultStepMixin_ItemStackLike.java deleted file mode 100644 index 29d7ced7021..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/smithing/SmithingRecipe_Builder_ResultStepMixin_ItemStackLike.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.api.itemstacklike.item.recipe.smithing; - -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.item.recipe.crafting.RecipeInput; -import org.spongepowered.api.item.recipe.smithing.SmithingRecipe; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.function.Function; - -@Mixin(value = SmithingRecipe.Builder.ResultStep.class, remap = false) -public interface SmithingRecipe_Builder_ResultStepMixin_ItemStackLike { - - default SmithingRecipe.Builder.EndStep result(ItemStackSnapshot result) { - return ((SmithingRecipe.Builder.ResultStep) this).result(result); - } - - default SmithingRecipe.Builder.EndStep result(ItemStack result) { - return ((SmithingRecipe.Builder.ResultStep) this).result(result); - } - - default SmithingRecipe.Builder.EndStep result(Function resultFunction, ItemStack exemplaryResult) { - return ((SmithingRecipe.Builder.ResultStep) this).result(resultFunction, exemplaryResult); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/AdvancementMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/AdvancementMixin_API.java index 79d4e5c2ac3..7979b5e7e17 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/AdvancementMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/AdvancementMixin_API.java @@ -24,27 +24,24 @@ */ package org.spongepowered.common.mixin.api.minecraft.advancements; -import com.google.gson.JsonElement; import net.kyori.adventure.text.Component; import net.minecraft.advancements.Advancement; import net.minecraft.advancements.AdvancementRequirements; import net.minecraft.advancements.AdvancementRewards; -import net.minecraft.advancements.Criterion; import net.minecraft.advancements.DisplayInfo; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.advancements.triggers.Criterion; +import net.minecraft.resources.Identifier; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.advancement.criteria.AdvancementCriterion; import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.Queries; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.advancement.SpongeAdvancementTemplate; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.advancements.AdvancementBridge; +import org.spongepowered.common.util.DataPackUtil; -import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Optional; @@ -52,7 +49,7 @@ @Mixin(Advancement.class) public abstract class AdvancementMixin_API implements org.spongepowered.api.advancement.Advancement { - @Shadow @Final private Optional parent; + @Shadow @Final private Optional parent; @Shadow @Final private Optional display; @Shadow @Final private AdvancementRewards rewards; @Shadow @Final private Map> criteria; @@ -87,19 +84,7 @@ public Component asComponent() { } @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement json = SpongeAdvancementTemplate.encode((Advancement) (Object) this); - try { - final DataContainer container = DataFormats.JSON.get().read(json.toString()); - container.set(Queries.CONTENT_VERSION, this.contentVersion()); - return container; - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Advancement:\n" + json, e); - } + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, Advancement.CODEC, (Advancement) (Object) this); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/AdvancementNodeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/AdvancementNodeMixin_API.java index c6322958f92..0fe2090c472 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/AdvancementNodeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/AdvancementNodeMixin_API.java @@ -28,10 +28,9 @@ import net.minecraft.advancements.AdvancementHolder; import net.minecraft.advancements.AdvancementNode; import net.minecraft.advancements.DisplayInfo; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.advancement.Advancement; -import org.spongepowered.api.advancement.AdvancementTemplate; import org.spongepowered.api.advancement.AdvancementTree; import org.spongepowered.api.advancement.TreeLayoutElement; import org.spongepowered.asm.mixin.Final; @@ -92,23 +91,24 @@ public Collection layoutElements() { } @Override - public Optional layoutElement(final AdvancementTemplate advancement) { - if (advancement.advancement().displayInfo().isEmpty()) { + public Optional layoutElement(final Advancement advancement) { + if (advancement.displayInfo().isEmpty()) { return Optional.empty(); } - return this.layoutElement(advancement.key()); + return this.impl$findElementInfo((AdvancementNode) (Object) this, (net.minecraft.advancements.Advancement) (Object) advancement) + .map(TreeLayoutElement.class::cast); } @Override public Optional layoutElement(final ResourceKey advancementKey) { - return this.impl$element(((ResourceLocation) (Object) advancementKey)); + return this.impl$element(((Identifier) (Object) advancementKey)); } - private Optional impl$element(final ResourceLocation loc) { + private Optional impl$element(final Identifier loc) { return this.impl$findElementInfo((AdvancementNode) (Object) this, loc).map(TreeLayoutElement.class::cast); } - private Optional impl$findElementInfo(AdvancementNode node, ResourceLocation key) { + private Optional impl$findElementInfo(AdvancementNode node, Identifier key) { if (node.holder().id().equals(key)) { return node.advancement().display(); } @@ -120,4 +120,17 @@ public Optional layoutElement(final ResourceKey advancementKe } return Optional.empty(); } + + private Optional impl$findElementInfo(AdvancementNode node, net.minecraft.advancements.Advancement advancement) { + if (node.holder().value() == advancement) { + return node.advancement().display(); + } + for (final AdvancementNode child : node.children()) { + final var info = this.impl$findElementInfo(child, advancement); + if (info.isPresent()) { + return info; + } + } + return Optional.empty(); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionMixin_API.java index a60236f044a..cbd75628439 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionMixin_API.java @@ -24,9 +24,9 @@ */ package org.spongepowered.common.mixin.api.minecraft.advancements; -import net.minecraft.advancements.Criterion; -import net.minecraft.advancements.CriterionTrigger; import net.minecraft.advancements.CriterionTriggerInstance; +import net.minecraft.advancements.triggers.Criterion; +import net.minecraft.advancements.triggers.CriterionTrigger; import org.spongepowered.api.advancement.criteria.trigger.FilteredTrigger; import org.spongepowered.api.advancement.criteria.trigger.Trigger; import org.spongepowered.asm.mixin.Final; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionProgressMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionProgressMixin_API.java index 134a08854fc..c60287fe7b0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionProgressMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionProgressMixin_API.java @@ -27,7 +27,7 @@ import net.minecraft.advancements.Advancement; import net.minecraft.advancements.AdvancementHolder; import net.minecraft.advancements.CriterionProgress; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.advancement.AdvancementProgress; import org.spongepowered.api.advancement.criteria.AdvancementCriterion; @@ -65,7 +65,7 @@ public Instant grant() { return this.obtained; } final AdvancementProgress advancementProgress = ((CriterionProgressBridge) this).bridge$getAdvancementProgress(); - final ResourceLocation key = ((AdvancementProgressBridge) advancementProgress).bridge$getAdvancementKey(); + final Identifier key = ((AdvancementProgressBridge) advancementProgress).bridge$getAdvancementKey(); final org.spongepowered.api.advancement.Advancement advancement = advancementProgress.advancement(); final AdvancementHolder holder = new AdvancementHolder(key, (Advancement) (Object) advancement); ((AdvancementProgressBridge) advancementProgress).bridge$getPlayerAdvancements().award(holder, this.criterion().name()); @@ -79,7 +79,7 @@ public Optional revoke() { } final Instant instant = this.obtained; final AdvancementProgress advancementProgress = ((CriterionProgressBridge) this).bridge$getAdvancementProgress(); - final ResourceLocation key = ((AdvancementProgressBridge) advancementProgress).bridge$getAdvancementKey(); + final Identifier key = ((AdvancementProgressBridge) advancementProgress).bridge$getAdvancementKey(); final org.spongepowered.api.advancement.Advancement advancement = advancementProgress.advancement(); final AdvancementHolder holder = new AdvancementHolder(key, (Advancement) (Object) advancement); ((AdvancementProgressBridge) advancementProgress).bridge$getPlayerAdvancements().revoke(holder, this.criterion().name()); diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionTriggerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionTriggerMixin_API.java index 17d1baf379c..2f01614e81f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionTriggerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/CriterionTriggerMixin_API.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.mixin.api.minecraft.advancements; -import net.minecraft.advancements.CriterionTrigger; +import net.minecraft.advancements.triggers.CriterionTrigger; import org.spongepowered.api.Sponge; import org.spongepowered.api.advancement.criteria.trigger.FilteredTriggerConfiguration; import org.spongepowered.api.advancement.criteria.trigger.Trigger; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/DisplayInfoMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/DisplayInfoMixin_API.java index 110ea019c19..8e2c102a228 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/DisplayInfoMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/advancements/DisplayInfoMixin_API.java @@ -26,7 +26,7 @@ import net.kyori.adventure.text.Component; import net.minecraft.advancements.DisplayInfo; -import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import org.spongepowered.api.advancement.Advancement; import org.spongepowered.api.advancement.AdvancementType; import org.spongepowered.api.advancement.TreeLayoutElement; @@ -56,7 +56,7 @@ public abstract class DisplayInfoMixin_API implements TreeLayoutElement, org.spo @Shadow public abstract boolean shadow$shouldAnnounceChat(); @Shadow public abstract boolean shadow$isHidden(); - @Shadow @Final private ItemStack icon; + @Shadow @Final private ItemStackTemplate icon; @Shadow @Final private boolean hidden; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/AbstractBlockMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/BlockBehaviourMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/AbstractBlockMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/BlockBehaviourMixin_API.java index 9de7530be58..d1282ec8124 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/AbstractBlockMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/BlockBehaviourMixin_API.java @@ -35,12 +35,14 @@ import org.spongepowered.asm.mixin.Shadow; @Mixin(value = BlockBehaviour.class) -public abstract class AbstractBlockMixin_API implements BlockType { +public abstract class BlockBehaviourMixin_API implements BlockType { // @formatter:off @Shadow @Final @org.spongepowered.asm.mixin.Mutable protected boolean isRandomlyTicking; @Shadow public abstract Item shadow$asItem(); @Shadow @Final protected SoundType soundType; + @Shadow public abstract String shadow$getDescriptionId(); + // @formatter:on @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/BlockMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/BlockMixin_API.java index 3ad68ef92b5..7126ff3910d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/BlockMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/BlockMixin_API.java @@ -27,7 +27,6 @@ import com.google.common.collect.ImmutableList; import net.kyori.adventure.text.Component; import net.minecraft.core.Holder; -import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Block; @@ -35,10 +34,7 @@ import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.BlockType; import org.spongepowered.api.item.ItemType; -import org.spongepowered.api.registry.DefaultedRegistryType; -import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.state.StateProperty; -import org.spongepowered.api.tag.Tag; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -48,16 +44,14 @@ import java.util.Collection; import java.util.Optional; import java.util.function.Supplier; -import java.util.stream.Collectors; @Mixin(value = Block.class, priority = 999) -public abstract class BlockMixin_API extends AbstractBlockMixin_API implements SpongeImmutableDataHolder { +public abstract class BlockMixin_API extends BlockBehaviourMixin_API implements SpongeImmutableDataHolder { // @formatter:off @Shadow @Final protected StateDefinition stateDefinition; @Shadow @Final private Holder.Reference builtInRegistryHolder; - @Shadow public abstract String shadow$getDescriptionId(); @Shadow public abstract net.minecraft.world.level.block.state.BlockState shadow$defaultBlockState(); // @formatter:on @@ -105,19 +99,4 @@ public boolean isAnyOf(final Supplier... types) { public boolean isAnyOf(final BlockType... types) { return Arrays.stream(types).anyMatch(type -> type == this); } - - @Override - public DefaultedRegistryType registryType() { - return RegistryTypes.BLOCK_TYPE; - } - - @Override - public Collection> tags() { - return this.registryType().get().tags().filter(this::is).collect(Collectors.toSet()); - } - - @Override - public boolean is(Tag tag) { - return this.builtInRegistryHolder.is((TagKey) (Object) tag); - } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/BlockSoundGroupMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/BlockSoundGroupMixin_API.java index 69aa244b41b..8c72f740ca9 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/BlockSoundGroupMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/block/BlockSoundGroupMixin_API.java @@ -56,26 +56,26 @@ public double pitch() { @Override public org.spongepowered.api.effect.sound.SoundType breakSound() { - return (org.spongepowered.api.effect.sound.SoundType) this.breakSound; + return (org.spongepowered.api.effect.sound.SoundType) (Object) this.breakSound; } @Override public org.spongepowered.api.effect.sound.SoundType stepSound() { - return (org.spongepowered.api.effect.sound.SoundType) this.stepSound; + return (org.spongepowered.api.effect.sound.SoundType) (Object) this.stepSound; } @Override public org.spongepowered.api.effect.sound.SoundType placeSound() { - return (org.spongepowered.api.effect.sound.SoundType) this.placeSound; + return (org.spongepowered.api.effect.sound.SoundType) (Object) this.placeSound; } @Override public org.spongepowered.api.effect.sound.SoundType hitSound() { - return (org.spongepowered.api.effect.sound.SoundType) this.hitSound; + return (org.spongepowered.api.effect.sound.SoundType) (Object) this.hitSound; } @Override public org.spongepowered.api.effect.sound.SoundType fallSound() { - return (org.spongepowered.api.effect.sound.SoundType) this.fallSound; + return (org.spongepowered.api.effect.sound.SoundType) (Object) this.fallSound; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/client/multiplayer/ClientLevelMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/client/multiplayer/ClientLevelMixin_API.java index 12f66296e46..2750caf8ca2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/client/multiplayer/ClientLevelMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/client/multiplayer/ClientLevelMixin_API.java @@ -70,7 +70,7 @@ public boolean isLoaded() { public ChunkLayout chunkLayout() { if (this.api$chunkLayout == null) { final var height = ((ClientLevel) (Object) this).getHeight(); - final var min = ((ClientLevel) (Object) this).getMinBuildHeight(); + final var min = ((ClientLevel) (Object) this).getMinY(); this.api$chunkLayout = new SpongeChunkLayout(min, height); } return this.api$chunkLayout; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/arguments/selector/EntitySelectorMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/arguments/selector/EntitySelectorMixin_API.java index 245d0d91b4f..c4c927d18ff 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/arguments/selector/EntitySelectorMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/arguments/selector/EntitySelectorMixin_API.java @@ -28,6 +28,7 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.arguments.selector.EntitySelector; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.Sponge; import org.spongepowered.api.command.CommandCause; @@ -63,7 +64,7 @@ public Collection select(@NonNull final ServerLocation location) throws @Override @NonNull public Collection select(@NonNull final Entity entity) throws IllegalStateException { - return this.api$select(((net.minecraft.world.entity.Entity) entity).createCommandSourceStack()); + return this.api$select(((net.minecraft.world.entity.Entity) entity).createCommandSourceStackForNameResolution(((ServerLevel) ((net.minecraft.world.entity.Entity) entity).level()))); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/arguments/selector/EntitySelectorParserMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/arguments/selector/EntitySelectorParserMixin_API.java index 2de473599c8..ef4d0024d43 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/arguments/selector/EntitySelectorParserMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/arguments/selector/EntitySelectorParserMixin_API.java @@ -29,8 +29,7 @@ import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; -import net.minecraft.advancements.critereon.MinMaxBounds; -import net.minecraft.advancements.critereon.WrappedMinMaxBounds; +import net.minecraft.advancements.predicates.MinMaxBounds; import net.minecraft.commands.arguments.selector.EntitySelector; import net.minecraft.commands.arguments.selector.EntitySelectorParser; import net.minecraft.core.registries.Registries; @@ -58,8 +57,6 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.accessor.advancements.critereon.MinMaxBounds_DoublesAccessor; -import org.spongepowered.common.accessor.advancements.critereon.MinMaxBounds_IntsAccessor; import org.spongepowered.common.bridge.commands.arguments.selector.EntitySelectorParserBridge; import org.spongepowered.common.command.selector.SpongeSelectorSortAlgorithm; import org.spongepowered.common.util.Preconditions; @@ -69,7 +66,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; @@ -96,30 +92,17 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder @Shadow @Nullable private Double deltaX; @Shadow @Nullable private Double deltaY; @Shadow @Nullable private Double deltaZ; - @Shadow private WrappedMinMaxBounds rotX = WrappedMinMaxBounds.ANY; - @Shadow private WrappedMinMaxBounds rotY = WrappedMinMaxBounds.ANY; + @Shadow private MinMaxBounds.FloatDegrees rotX = MinMaxBounds.FloatDegrees.ANY; + @Shadow private MinMaxBounds.FloatDegrees rotY = MinMaxBounds.FloatDegrees.ANY; @Shadow @Final private List> predicates; @Shadow private boolean currentEntity; @Shadow @Nullable private String playerName; @Shadow private int startPosition; @Shadow @Nullable private UUID entityUUID; @Shadow private BiFunction, CompletableFuture> suggestions; - @Shadow private boolean hasNameEquals; - @Shadow private boolean hasNameNotEquals; - @Shadow private boolean isLimited; - @Shadow private boolean isSorted; - @Shadow private boolean hasGamemodeEquals; - @Shadow private boolean hasGamemodeNotEquals; - @Shadow private boolean hasTeamEquals; - @Shadow private boolean hasTeamNotEquals; @Shadow private net.minecraft.world.entity.@Nullable EntityType type; - @Shadow private boolean typeInverse; - @Shadow private boolean hasScores; - @Shadow private boolean hasAdvancements; @Shadow private boolean usesSelectors; - @Shadow public abstract void shadow$setLimited(boolean value); - @Shadow public abstract void shadow$setSorted(boolean value); @Shadow public abstract void shadow$setX(double xIn); @Shadow public abstract void shadow$setY(double yIn); @Shadow public abstract void shadow$setZ(double zIn); @@ -131,9 +114,12 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder @Shadow public abstract void shadow$addPredicate(Predicate p_197401_1_); // @formatter:on - @Nullable private Map> api$scores; - @Nullable private Object2BooleanOpenHashMap api$advancement; - @Nullable private Map> api$criterion; + @Nullable + private Map> api$scores; + @Nullable + private Object2BooleanOpenHashMap api$advancement; + @Nullable + private Map> api$criterion; private boolean api$forceSelf; @Override @@ -160,7 +146,6 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder @Override public Selector.@NonNull Builder limit(final int limit) { this.maxResults = limit; - this.shadow$setLimited(limit != Integer.MAX_VALUE); return this; } @@ -172,13 +157,26 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder if (range.max() != null && range.max() < 0) { throw new IllegalArgumentException("max must be non-negative"); } - this.distance = MinMaxBounds_DoublesAccessor.invoker$new(Optional.ofNullable(range.min()), Optional.ofNullable(range.max())); + + final Double min = range.min(); + final Double max = range.max(); + if (min == null && max == null) { + this.distance = MinMaxBounds.Doubles.ANY; + } else if (min == null) { + this.distance = MinMaxBounds.Doubles.atMost(max); + } else if (max == null) { + this.distance = MinMaxBounds.Doubles.atLeast(min); + } else if (min <= max) { + this.distance = MinMaxBounds.Doubles.between(min, max); + } else { + this.distance = MinMaxBounds.Doubles.between(max, min); + } return this; } @Override public Selector.@NonNull Builder volume(final org.spongepowered.math.vector.@NonNull Vector3d corner1, - final org.spongepowered.math.vector.@NonNull Vector3d corner2) { + final org.spongepowered.math.vector.@NonNull Vector3d corner2) { final org.spongepowered.math.vector.Vector3d minPoint = corner1.min(corner2); final org.spongepowered.math.vector.Vector3d distance = corner1.max(corner2).sub(minPoint); this.shadow$setX(minPoint.x()); @@ -198,7 +196,6 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder @Override public Selector.@NonNull Builder sortAlgorithm(final @NonNull SelectorSortAlgorithm algorithm) { org.spongepowered.common.util.Preconditions.checkArgument(algorithm instanceof SpongeSelectorSortAlgorithm, "Must be a SpongeSelectorSortAlgorithm"); - this.shadow$setSorted(true); this.order = ((SpongeSelectorSortAlgorithm) algorithm).getSortAlgorithm(); return this; } @@ -220,7 +217,7 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder @Override public Selector.@NonNull Builder addNotAdvancementCriterion(final @NonNull ResourceKey advancement, - final @NonNull AdvancementCriterion criterion) { + final @NonNull AdvancementCriterion criterion) { return this.api$advancementCriterion(advancement, criterion, true); } @@ -265,7 +262,19 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder public Selector.@NonNull Builder experienceLevel(final @NonNull Range<@NonNull Integer> range) { Preconditions.checkArgument(range.min() == null || range.min() >= 0, "min must be non-negative"); Preconditions.checkArgument(range.max() == null || range.max() >= 0, "max must be non-negative"); - this.level = MinMaxBounds_IntsAccessor.invoker$new(Optional.of(range.min()), Optional.of(range.max())); + final Integer min = range.min(); + final Integer max = range.max(); + if (min == null && max == null) { + this.level = MinMaxBounds.Ints.ANY; + } else if (min == null) { + this.level = MinMaxBounds.Ints.atMost(max); + } else if (max == null) { + this.level = MinMaxBounds.Ints.atLeast(min); + } else if (min <= max) { + this.level = MinMaxBounds.Ints.between(min, max); + } else { + this.level = MinMaxBounds.Ints.between(max, min); + } this.shadow$setIncludesEntities(false); return this; } @@ -379,7 +388,7 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder } if (this.api$criterion != null) { this.api$criterion.forEach((key, value) -> - value.object2BooleanEntrySet().fastForEach(x -> entries.add(key + "={" + x.getKey() + "=" + x.getBooleanValue() + "}"))); + value.object2BooleanEntrySet().fastForEach(x -> entries.add(key + "={" + x.getKey() + "=" + x.getBooleanValue() + "}"))); } this.api$handle("advancements", "{" + String.join(",", entries) + "}"); this.api$advancement = null; @@ -413,25 +422,14 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder this.deltaX = null; this.deltaY = null; this.deltaZ = null; - this.rotX = WrappedMinMaxBounds.ANY; - this.rotY = WrappedMinMaxBounds.ANY; + this.rotX = MinMaxBounds.FloatDegrees.ANY; + this.rotY = MinMaxBounds.FloatDegrees.ANY; this.predicates.clear(); this.currentEntity = false; this.playerName = null; this.startPosition = 0; this.entityUUID = null; - this.hasNameEquals = false; - this.hasNameNotEquals = false; - this.isLimited = false; - this.isSorted = false; - this.hasGamemodeEquals = false; - this.hasGamemodeNotEquals = false; - this.hasTeamEquals = false; - this.hasTeamNotEquals = false; this.type = null; - this.typeInverse = false; - this.hasScores = false; - this.hasAdvancements = false; this.usesSelectors = false; this.reader.setCursor(0); this.suggestions = EntitySelectorParser.SUGGEST_NOTHING; @@ -448,7 +446,7 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder } private Selector.@NonNull Builder api$advancementCriterion(final @NonNull ResourceKey advancement, final @NonNull AdvancementCriterion criterion, - final boolean inverted) { + final boolean inverted) { if (this.api$criterion == null) { this.api$criterion = new HashMap<>(); } @@ -465,9 +463,9 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder ((EntitySelectorParserBridge) this).bridge$handleValue(name, value, invert); } catch (final CommandSyntaxException ex) { throw new IllegalArgumentException( - String.format("Could not create selector criteria based on input (name = '%s', value = '%s', invert = %s)", name, value, - invert.name()), - ex); + String.format("Could not create selector criteria based on input (name = '%s', value = '%s', invert = %s)", name, value, + invert.name()), + ex); } } @@ -480,19 +478,19 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder } - private WrappedMinMaxBounds api$getWrappedBounds(final Range<@NonNull Double> range) { + private MinMaxBounds.FloatDegrees api$getWrappedBounds(final Range<@NonNull Double> range) { final Float a = this.api$floatFromDouble(range.min(), Mth::wrapDegrees); final Float b = this.api$floatFromDouble(range.max(), Mth::wrapDegrees); if (a == null) { - return new WrappedMinMaxBounds(null, b); + return new MinMaxBounds.FloatDegrees(MinMaxBounds.Bounds.atMost(b)); } if (b == null) { - return new WrappedMinMaxBounds(a, null); + return new MinMaxBounds.FloatDegrees(MinMaxBounds.Bounds.atLeast(a)); } if (a <= b) { - return new WrappedMinMaxBounds(a, b); + return new MinMaxBounds.FloatDegrees(MinMaxBounds.Bounds.between(a, b)); } - return new WrappedMinMaxBounds(b, a); + return new MinMaxBounds.FloatDegrees(MinMaxBounds.Bounds.between(b, a)); } private String api$intRangeToStringRepresentation(final @NonNull Range<@NonNull Integer> range) { @@ -501,8 +499,8 @@ public abstract class EntitySelectorParserMixin_API implements Selector.Builder } return String.format("%s..%s", - range.min() == null ? "" : String.valueOf(range.min().intValue()), - range.max() == null ? "" : String.valueOf(range.max().intValue())); + range.min() == null ? "" : String.valueOf(range.min().intValue()), + range.max() == null ? "" : String.valueOf(range.max().intValue())); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/synchronization/SuggestionProviders_WrapperMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/synchronization/SuggestionProviders_WrapperMixin_API.java index 98070be4dbe..9f6f6135387 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/synchronization/SuggestionProviders_WrapperMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/commands/synchronization/SuggestionProviders_WrapperMixin_API.java @@ -41,7 +41,7 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; -@Mixin(targets = "net/minecraft/commands/synchronization/SuggestionProviders$Wrapper") +@Mixin(targets = "net/minecraft/commands/synchronization/SuggestionProviders$RegisteredSuggestion") public abstract class SuggestionProviders_WrapperMixin_API implements CommandCompletionProvider { // @formatter:off diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/core/MappedRegistryMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/core/MappedRegistryMixin_API.java index b6f870bd4a5..62455d8f24e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/core/MappedRegistryMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/core/MappedRegistryMixin_API.java @@ -28,9 +28,8 @@ import net.minecraft.core.HolderSet; import net.minecraft.core.MappedRegistry; import net.minecraft.core.RegistrationInfo; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.tags.TagKey; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.registry.Registry; import org.spongepowered.api.registry.RegistryEntry; @@ -44,11 +43,10 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.bridge.core.RegistryBridge; import org.spongepowered.common.bridge.core.WritableRegistryBridge; +import org.spongepowered.common.bridge.tags.TagBridge; import java.util.Objects; import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; import java.util.stream.Stream; @Mixin(MappedRegistry.class) @@ -58,22 +56,17 @@ public abstract class MappedRegistryMixin_API implements Registry { @Shadow public abstract Holder.Reference shadow$register(final net.minecraft.resources.ResourceKey $$0, final T $$1, final RegistrationInfo $$2); @Shadow public abstract net.minecraft.resources.ResourceKey> shadow$key(); - @Shadow @Nullable public abstract T shadow$get(@Nullable ResourceLocation var1); - private ResourceLocation impl$getKey(final T value) { + private Identifier impl$getKey(final T value) { return ((net.minecraft.core.Registry) this).getKey(value); } - private Optional impl$getOptional(@Nullable ResourceLocation param0) { - return ((net.minecraft.core.Registry) this).getOptional(param0); - } - private Optional> impl$getTag(TagKey var1) { - return ((net.minecraft.core.Registry) this).getTag(var1); + return ((net.minecraft.core.Registry) this).get(var1); } private Stream> impl$getTagNames() { - return ((net.minecraft.core.Registry) this).getTagNames(); + return ((net.minecraft.core.Registry) this).getTags().map(HolderSet.Named::key); } @Override @@ -85,7 +78,7 @@ public RegistryType type() { public ResourceKey valueKey(final T value) { Objects.requireNonNull(value, "value"); - final ResourceLocation key = this.impl$getKey(value); + final Identifier key = this.impl$getKey(value); if (key == null) { throw new IllegalStateException(String.format("No key was found for '%s'!", value)); } @@ -107,16 +100,18 @@ public Optional> findEntry(final ResourceKey key) return ((RegistryBridge) this).bridge$get(Objects.requireNonNull(key, "key")); } + @SuppressWarnings("unchecked") @Override public Optional findValue(final ResourceKey key) { - return (Optional) this.impl$getOptional((ResourceLocation) (Object) Objects.requireNonNull(key, "key")); + return ((RegistryBridge) this).bridge$get(Objects.requireNonNull(key, "key")).map(RegistryEntry::value); } + @SuppressWarnings("unchecked") @Override public V value(final ResourceKey key) { - final V value = (V) this.shadow$get((ResourceLocation) (Object) Objects.requireNonNull(key, "key")); + final RegistryEntry value = ((RegistryBridge) this).bridge$getEntry(Objects.requireNonNull(key, "key")); if (value != null) { - return value; + return value.value(); } throw new ValueNotFoundException(String.format("No value was found for key '%s'!", key)); @@ -124,11 +119,10 @@ public V value(final ResourceKey key) { @Override @SuppressWarnings("unchecked") - public Set taggedValues(final Tag tag) { - return this.impl$getTag((TagKey) (Object) tag).stream() + public Stream taggedValues(final Tag tag) { + return this.impl$getTag(((TagBridge) tag).bridge$asVanillaTag()).stream() .flatMap(HolderSet.ListBacked::stream) - .map(h -> (V) h.value()) - .collect(Collectors.toSet()); + .map(h -> (V) h.value()); } @Override @@ -161,7 +155,7 @@ public Optional> register(final ResourceKey key, Objects.requireNonNull(value, "value"); if (this.isDynamic()) { - final net.minecraft.resources.ResourceKey mcKey = net.minecraft.resources.ResourceKey.create(this.shadow$key(), (ResourceLocation) (Object) key); + final net.minecraft.resources.ResourceKey mcKey = net.minecraft.resources.ResourceKey.create(this.shadow$key(), (Identifier) (Object) key); this.shadow$register(mcKey, value, RegistrationInfo.BUILT_IN); return ((RegistryBridge) this).bridge$get(key); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/core/RegistryMixin.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/core/RegistryMixin.java new file mode 100644 index 00000000000..6e40f5569f1 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/core/RegistryMixin.java @@ -0,0 +1,88 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.core; + +import net.minecraft.core.Registry; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import org.spongepowered.api.registry.RegistryEntry; +import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.api.registry.ValueNotFoundException; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.registry.SpongeRegistryEntry; + +import java.util.Objects; +import java.util.Optional; + +import javax.annotation.Nullable; + +@Mixin(Registry.class) +public interface RegistryMixin extends org.spongepowered.api.registry.Registry { + + @Shadow ResourceKey> shadow$key(); + + @Shadow @Nullable Identifier shadow$getKey(T object); + + @Shadow Optional shadow$getOptional(@org.jetbrains.annotations.Nullable Identifier arg); + + @Shadow @Nullable T shadow$getValue(@org.jetbrains.annotations.Nullable Identifier arg); + + @Override + default RegistryType type() { + return RegistryType.of((org.spongepowered.api.ResourceKey) (Object) this.shadow$key().registry(), (org.spongepowered.api.ResourceKey) (Object) this.shadow$key().identifier()); + } + + @Override + default org.spongepowered.api.ResourceKey valueKey(final T value) { + return (org.spongepowered.api.ResourceKey) (Object) this.shadow$getKey(Objects.requireNonNull(value, "value")); + } + + @Override + default Optional findValueKey(final T value) { + return Optional.ofNullable(this.valueKey(Objects.requireNonNull(value, "value"))); + } + + @Override + default Optional> findEntry(final org.spongepowered.api.ResourceKey key) { + return this.shadow$getOptional((Identifier) (Object) Objects.requireNonNull(key, "key")).map(e -> + new SpongeRegistryEntry<>((RegistryType) this.type(), key, (V) e)); + } + + @Override + default Optional findValue(final org.spongepowered.api.ResourceKey key) { + return (Optional) this.shadow$getOptional((Identifier) (Object) Objects.requireNonNull(key, "key")); + } + + @Override + default V value(final org.spongepowered.api.ResourceKey key) { + final V value = (V) this.shadow$getValue((Identifier) (Object) Objects.requireNonNull(key, "key")); + if (value != null) { + return value; + } + + throw new ValueNotFoundException(String.format("No value was found for key '%s'!", key)); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/SnowGolemMixin.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/item/AirItemMixin_API.java similarity index 66% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/SnowGolemMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/item/AirItemMixin_API.java index 0bd8e652526..6cbbc805423 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/SnowGolemMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/item/AirItemMixin_API.java @@ -22,25 +22,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.item; -import net.minecraft.world.entity.animal.SnowGolem; -import net.minecraft.world.level.GameRules; +import net.minecraft.world.item.AirItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Blocks; +import org.spongepowered.api.block.BlockType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.common.bridge.world.entity.GrieferBridge; -import org.spongepowered.common.mixin.core.world.entity.MobMixin; +import org.spongepowered.common.mixin.api.minecraft.world.item.ItemMixin_API; -@Mixin(SnowGolem.class) -public abstract class SnowGolemMixin extends MobMixin { +@Mixin(AirItem.class) +public abstract class AirItemMixin_API extends ItemMixin_API { - @Inject(method = "aiStep()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;floor(D)I", ordinal = 3), - cancellable = true) - private void impl$onCanGrief(CallbackInfo ci) { - if (!this.shadow$level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || !((GrieferBridge) this).bridge$canGrief()) { - ci.cancel(); - } + @Inject(method = "", at = @At(value = "RETURN")) + private void api$setBlockType(Item.Properties builder, CallbackInfo ci) { + this.api$blockType = (BlockType) Blocks.AIR; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/item/BlockItemMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/item/BlockItemMixin_API.java index 47e4cf772c3..23df0926a70 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/item/BlockItemMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/item/BlockItemMixin_API.java @@ -24,7 +24,6 @@ */ package org.spongepowered.common.mixin.api.minecraft.item; -import net.minecraft.world.item.AirItem; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; @@ -35,7 +34,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.mixin.api.minecraft.world.item.ItemMixin_API; -@Mixin(value = { BlockItem.class, AirItem.class }) +@Mixin(BlockItem.class) public abstract class BlockItemMixin_API extends ItemMixin_API { @Inject(method = "", at = @At(value = "RETURN")) diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/map/MapDecorationMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/map/MapDecorationMixin_API.java index e1a76fb46eb..c0710819bb3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/map/MapDecorationMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/map/MapDecorationMixin_API.java @@ -24,9 +24,15 @@ */ package org.spongepowered.common.mixin.api.minecraft.map; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; +import com.mojang.serialization.JsonOps; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.world.level.saveddata.maps.MapDecoration; import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.map.decoration.MapDecorationType; @@ -35,6 +41,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.world.storage.MapDecorationBridge; import org.spongepowered.common.map.decoration.orientation.SpongeMapDecorationOrientation; @@ -56,6 +63,15 @@ public abstract class MapDecorationMixin_API implements org.spongepowered.api.ma @Shadow @Final private Optional name; // @formatter:on + @Unique + private static final Gson impl$gson = impl$gson(); + + @Unique + private static Gson impl$gson() { + final GsonBuilder builder = new GsonBuilder().disableHtmlEscaping(); + GsonComponentSerializer.gson().populator().apply(builder); + return builder.create(); + } @Override public MapDecorationType type() { @@ -106,7 +122,12 @@ public DataContainer toContainer() { .set(Constants.Map.DECORATION_X, this.x) .set(Constants.Map.DECORATION_Y, this.y) .set(Constants.Map.DECORATION_ROTATION, (byte) MapUtil.normalizeDecorationOrientation(this.rot)); - this.name.ifPresent(component -> data.set(Constants.Map.NAME, Component.Serializer.toJson(component, SpongeCommon.server().registryAccess()))); + this.name.ifPresent(component -> { + final var json = ComponentSerialization.CODEC + .encodeStart(SpongeCommon.scopedHolder().createSerializationContext(JsonOps.INSTANCE), component) + .getOrThrow(JsonParseException::new); + data.set(Constants.Map.NAME, impl$gson.toJson(json)); + }); return data; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/network/chat/ChatTypeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/network/chat/ChatTypeMixin_API.java index 46d50d5e73f..73933748089 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/network/chat/ChatTypeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/network/chat/ChatTypeMixin_API.java @@ -32,10 +32,13 @@ import net.minecraft.network.chat.Component; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.adventure.SpongeAdventure; +import org.spongepowered.common.util.DataPackUtil; import java.util.Optional; @@ -91,4 +94,9 @@ public static abstract class BoundMixin_API implements net.kyori.adventure.chat. } } + + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, ChatType.DIRECT_CODEC, (ChatType) (Object) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/resources/ResourceLocationMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/resources/IdentifierMixin_API.java similarity index 83% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/resources/ResourceLocationMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/resources/IdentifierMixin_API.java index 45fe71f93a4..e5b73b0e2f6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/resources/ResourceLocationMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/resources/IdentifierMixin_API.java @@ -25,26 +25,36 @@ package org.spongepowered.common.mixin.api.minecraft.resources; import net.kyori.adventure.key.Key; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.spongepowered.api.ResourceKey; import org.spongepowered.asm.mixin.Implements; import org.spongepowered.asm.mixin.Interface; import org.spongepowered.asm.mixin.Interface.Remap; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; -@Mixin(ResourceLocation.class) +@Mixin(Identifier.class) @Implements(value = { @Interface(iface = Key.class, prefix = "adventure$", remap = Remap.NONE), @Interface(iface = ResourceKey.class, prefix = "resourceKey$", remap = Remap.NONE) }) -public abstract class ResourceLocationMixin_API { +public abstract class IdentifierMixin_API { // @formatter:off @Shadow public abstract String shadow$getNamespace(); @Shadow public abstract String shadow$getPath(); // @formatter:on + /** + * @author MrHell228 - October 6th, 2025 + * @reason Delegate synthetic method to #compareTo(Key) to avoid CCE in case Key is not a Identifier + */ + @Overwrite + public int compareTo(final Object obj) { + return ((Key) this).compareTo((Key) obj); + } + public String adventure$namespace() { return this.shadow$getNamespace(); } @@ -52,8 +62,4 @@ public abstract class ResourceLocationMixin_API { public String adventure$value() { return this.shadow$getPath(); } - - public int adventure$compareTo(Key o) { - return -o.compareTo((Key) this); - } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/MinecraftServerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/MinecraftServerMixin_API.java index b274dd8e33f..94dd423ee8b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/MinecraftServerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/MinecraftServerMixin_API.java @@ -26,7 +26,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -import com.llamalad7.mixinextras.sugar.Local; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.identity.Identity; @@ -35,16 +34,13 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.minecraft.commands.Commands; import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerScoreboard; -import net.minecraft.server.WorldStem; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.packs.repository.PackRepository; import net.minecraft.server.players.PlayerList; -import net.minecraft.world.level.levelgen.WorldDimensions; -import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.storage.LevelResource; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.WorldData; @@ -81,6 +77,7 @@ import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.commands.CommandsBridge; import org.spongepowered.common.bridge.server.MinecraftServerBridge; +import org.spongepowered.common.bridge.world.level.storage.PrimaryLevelDataBridge; import org.spongepowered.common.command.manager.SpongeCommandManager; import org.spongepowered.common.command.sponge.SpongeCommand; import org.spongepowered.common.datapack.SpongeDataPackManager; @@ -115,7 +112,7 @@ public abstract class MinecraftServerMixin_API implements SpongeServer, SpongeRegistryHolder { // @formatter:off - @Shadow @Final public long[] tickTimesNanos; + @Shadow @Final private long[] tickTimesNanos; @Shadow @Final protected WorldData worldData; @Shadow public abstract net.minecraft.world.item.crafting.RecipeManager shadow$getRecipeManager(); @@ -124,17 +121,13 @@ public abstract class MinecraftServerMixin_API implements SpongeServer, SpongeRe @Shadow public abstract String shadow$getMotd(); @Shadow public abstract int shadow$getTickCount(); @Shadow public abstract void shadow$halt(boolean p_71263_1_); - @Shadow public abstract int shadow$getPlayerIdleTimeout(); @Shadow public abstract void shadow$setPlayerIdleTimeout(int p_143006_1_); @Shadow public abstract boolean shadow$isHardcore(); - @Shadow public abstract boolean shadow$isPvpAllowed(); - @Shadow public abstract boolean shadow$isCommandBlockEnabled(); - @Shadow public abstract boolean shadow$isSpawningMonsters(); - @Shadow public abstract boolean shadow$isSpawningAnimals(); @Shadow public abstract Commands shadow$getCommands(); @Shadow public abstract PackRepository shadow$getPackRepository(); @Shadow public abstract net.minecraft.server.packs.resources.ResourceManager shadow$getResourceManager(); @Shadow public abstract WorldData shadow$getWorldData(); + @Shadow public abstract GameRules shadow$getGameRules(); // @formatter:on @@ -146,6 +139,10 @@ public abstract class MinecraftServerMixin_API implements SpongeServer, SpongeRe @Shadow public abstract boolean isSingleplayer(); + @Shadow public abstract void shadow$setUsingWhitelist(boolean $$0); + + @Shadow public abstract int shadow$playerIdleTimeout(); + private Iterable audiences; private ServerScheduler api$scheduler; private SpongeWorldManager api$worldManager; @@ -156,19 +153,17 @@ public abstract class MinecraftServerMixin_API implements SpongeServer, SpongeRe private ServerScoreboard api$scoreboard; private SpongeGameProfileManager api$profileManager; private MapStorage api$mapStorage; - private RegistryHolderLogic api$registryHolder; private SpongeUserManager api$userManager; private SpongeDataPackManager api$dataPackManager; private final BlockDestructionIdCache api$blockDestructionIdCache = new BlockDestructionIdCache(0, AtomicInteger::decrementAndGet); @Inject(method = "", at = @At("TAIL")) - public void api$initializeSpongeFieldsfinal(final CallbackInfo ci, @Local(argsOnly = true) final WorldStem levelStem) { + public void api$initializeSpongeFieldsfinal(final CallbackInfo ci) { this.api$scheduler = new ServerScheduler(); this.api$worldManager = new SpongeWorldManager((MinecraftServer) (Object) this); this.api$playerDataHandler = new SpongePlayerDataManager(this); this.api$teleportHelper = new SpongeTeleportHelper(); this.api$mapStorage = new SpongeMapStorage(); - this.api$registryHolder = new RegistryHolderLogic(levelStem.registries().compositeAccess()); this.api$userManager = new SpongeUserManager((MinecraftServer) (Object) this); this.api$dataPackManager = new SpongeDataPackManager((MinecraftServer) (Object) this, this.storageSource.getLevelPath(LevelResource.DATAPACK_DIR)); } @@ -222,7 +217,7 @@ public boolean isWhitelistEnabled() { @Override public void setHasWhitelist(final boolean enabled) { - this.shadow$getPlayerList().setUsingWhiteList(enabled); + this.shadow$setUsingWhitelist(enabled); } @Override @@ -252,22 +247,22 @@ public boolean isGameModeEnforced() { @Override public boolean isPVPEnabled() { - return this.shadow$isPvpAllowed(); + return this.shadow$getGameRules().get(GameRules.PVP); } @Override public boolean areCommandBlocksEnabled() { - return this.shadow$isCommandBlockEnabled(); + return this.shadow$getGameRules().get(GameRules.COMMAND_BLOCKS_WORK); } @Override public boolean isMonsterSpawnsEnabled() { - return this.shadow$isSpawningMonsters(); + return this.shadow$getGameRules().get(GameRules.SPAWN_MONSTERS); } @Override public boolean isAnimalSpawnsEnabled() { - return this.shadow$isSpawningAnimals(); + return true; } /** @@ -275,17 +270,13 @@ public boolean isAnimalSpawnsEnabled() { */ @Override public boolean isMultiWorldEnabled() { - return this.isSingleplayer() || ((Object) this instanceof DedicatedServer ds) && ds.getProperties().allowNether; + return this.isSingleplayer() || ((Object) this instanceof DedicatedServer ds) && ds.getGameRules().get(GameRules.ALLOW_ENTERING_NETHER_USING_PORTALS); } @Override public WorldGenerationConfig worldGenerationConfig() { final WorldData overworldData = this.shadow$getWorldData(); - - final WorldGenSettings settings = new WorldGenSettings(overworldData.worldGenOptions(), - new WorldDimensions(this.registryAccess().registryOrThrow(Registries.LEVEL_STEM))); - - return (WorldGenerationConfig) (Object) settings; + return (WorldGenerationConfig) (Object) ((PrimaryLevelDataBridge) overworldData.overworldData()).bridge$spongeData().worldGenOptions(); } @Override @@ -424,7 +415,7 @@ public Optional serverScoreboard() { @Intrinsic public int server$playerIdleTimeout() { - return this.shadow$getPlayerIdleTimeout(); + return this.shadow$playerIdleTimeout(); } @Intrinsic @@ -486,6 +477,7 @@ public BlockDestructionIdCache getBlockDestructionIdCache() { return this.api$blockDestructionIdCache; } + @SuppressWarnings("UnstableApiUsage") @Override public void sendMessage(final Identity identity, final Component message, final MessageType type) { this.shadow$getPlayerList().broadcastSystemMessage(SpongeAdventure.asVanilla(message), false); @@ -503,6 +495,6 @@ public MapStorage mapStorage() { @Override public RegistryHolderLogic registryHolder() { - return this.api$registryHolder; + return ((MinecraftServerBridge) this).bridge$registryHolder(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ChunkMapMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ChunkMapMixin_API.java index f009a2dac26..951292ef152 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ChunkMapMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ChunkMapMixin_API.java @@ -67,7 +67,7 @@ public boolean valid(final @NonNull Ticket ticket) { @Override public @NonNull Optional> requestTicket(final @NonNull TicketType type, final @NonNull Vector3i chunkPosition, final @NonNull T value, final int radius) { - if (!(type instanceof net.minecraft.server.level.TicketType)) { + if (!((Object) type instanceof net.minecraft.server.level.TicketType)) { throw new IllegalArgumentException("TicketType must be a Minecraft TicketType"); } if (radius < 1) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerLevelMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerLevelMixin_API.java index f2f6761b2ae..c449a78f738 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerLevelMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerLevelMixin_API.java @@ -37,15 +37,13 @@ import net.minecraft.world.entity.raid.Raid; import net.minecraft.world.entity.raid.Raids; import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.CollisionGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionStorageInfo; -import net.minecraft.world.level.dimension.end.EndDragonFight; +import net.minecraft.world.level.dimension.end.EnderDragonFight; import net.minecraft.world.level.entity.PersistentEntitySectionManager; import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.storage.LevelResource; import net.minecraft.world.level.storage.ServerLevelData; import net.minecraft.world.ticks.LevelTicks; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -75,7 +73,7 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.accessor.world.entity.raid.RaidsAccessor; -import org.spongepowered.common.accessor.world.level.dimension.end.EndDragonFightAccessor; +import org.spongepowered.common.accessor.world.level.dimension.end.EnderDragonFightAccessor; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.server.level.ServerLevelBridge; import org.spongepowered.common.bridge.world.level.border.WorldBorderBridge; @@ -122,12 +120,12 @@ public abstract class ServerLevelMixin_API extends LevelMixin_API shadow$players(); @Shadow public abstract Raids shadow$getRaids(); @Nullable @Shadow public abstract Raid shadow$getRaidAt(BlockPos p_217475_1_); - @Nullable @Shadow public abstract EndDragonFight shadow$getDragonFight(); + @Nullable @Shadow public abstract EnderDragonFight shadow$getDragonFight(); @Shadow public abstract long shadow$getSeed(); + @Shadow public abstract net.minecraft.world.level.border.WorldBorder shadow$getWorldBorder(); // @formatter:on private volatile @MonotonicNonNull Pointers api$pointers; @@ -184,7 +182,7 @@ public ChunkGenerator generator() { @Override public ResourceKey key() { - return (ResourceKey) (Object) this.shadow$dimension().location(); + return (ResourceKey) (Object) this.shadow$dimension().identifier(); } @Override @@ -209,7 +207,7 @@ public boolean restoreSnapshot(final int x, final int y, final int z, final Bloc @Override public Path directory() { - return ((ServerLevelBridge) this).bridge$getLevelSave().getLevelPath(LevelResource.ROOT); + return ((ServerLevelBridge) this).bridge$getLevelSave().getDimensionPath(this.shadow$dimension()); } private boolean impl$save(final boolean flush) { @@ -265,9 +263,9 @@ public Optional raidAt(final Vector3i blockPosi @Override public Optional dragonFightBossBar() { - final @Nullable EndDragonFight fight = this.shadow$getDragonFight(); + final @Nullable EnderDragonFight fight = this.shadow$getDragonFight(); if (fight != null) { - return Optional.of(SpongeAdventure.asAdventure(((EndDragonFightAccessor) fight).accessor$dragonEvent())); + return Optional.of(SpongeAdventure.asAdventure(((EnderDragonFightAccessor) fight).accessor$dragonEvent())); } else { return Optional.empty(); } @@ -284,7 +282,7 @@ public boolean contains(final int x, final int y, final int z) { @Override public Optional entity(final UUID uniqueId) { - return Optional.ofNullable((org.spongepowered.api.entity.Entity) this.shadow$getEntity(Objects.requireNonNull(uniqueId, "uniqueId"))); + return Optional.ofNullable((org.spongepowered.api.entity.Entity) ((ServerLevel) (Object) this).getEntity(Objects.requireNonNull(uniqueId, "uniqueId"))); } // MutableBlockEntityVolume @@ -331,22 +329,22 @@ public void setWeather(final WeatherType type, final Ticks ticks) { public ChunkLayout chunkLayout() { if (this.api$chunkLayout == null) { final var height = ((ServerLevel) (Object) this).getHeight(); - final var min = ((ServerLevel) (Object) this).getMinBuildHeight(); + final var min = ((ServerLevel) (Object) this).getMinY(); this.api$chunkLayout = new SpongeChunkLayout(min, height); } return this.api$chunkLayout; } + @SuppressWarnings("deprecation") @Override public WorldBorder setBorder(final WorldBorder border) { - - final WorldBorderBridge borderBridge = (WorldBorderBridge) ((CollisionGetter) this).getWorldBorder(); + final var old = this.shadow$getWorldBorder(); + final WorldBorderBridge borderBridge = (WorldBorderBridge) old; borderBridge.bridge$setAssociatedWorld(this.key()); final WorldBorder worldBorder = borderBridge.bridge$applyFrom(border); if (worldBorder == null) { - return (WorldBorder) net.minecraft.world.level.border.WorldBorder.DEFAULT_SETTINGS; + return (WorldBorder) (Object) net.minecraft.world.level.border.WorldBorder.Settings.DEFAULT; } - this.serverLevelData.setWorldBorder((net.minecraft.world.level.border.WorldBorder.Settings) border); return worldBorder; } @@ -394,7 +392,7 @@ public Stream chunkPositions() { return this.api$chunkPosStream((regionFile, stream) -> // stream.filter(regionFile::doesChunkExist) // filter out non-existent chunks stream.filter(cp -> ((RegionFileBridge) regionFile).bridge$doesChunkExist(cp)) // filter out non-existent chunks - .map(cp -> new Vector3i(cp.x, 0, cp.z)) // map to API type + .map(cp -> new Vector3i(cp.x(), 0, cp.z())) // map to API type ); } @@ -418,6 +416,6 @@ public Stream offlineChunks() { final int ry = Integer.parseInt(split[2]); final ChunkPos min = ChunkPos.minFromRegion(rx, ry); final ChunkPos max = ChunkPos.maxFromRegion(rx, ry); - return new Vector4i(min.x, min.z, max.x, max.z); + return new Vector4i(min.x(), min.z(), max.x(), max.z()); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerPlayerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerPlayerMixin_API.java index 0d182ccf104..e438dd44399 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerPlayerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerPlayerMixin_API.java @@ -40,7 +40,7 @@ import net.minecraft.network.chat.PlayerChatMessage; import net.minecraft.network.protocol.game.ClientboundDeleteChatPacket; import net.minecraft.network.protocol.game.ClientboundInitializeBorderPacket; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.MinecraftServer; import net.minecraft.server.PlayerAdvancements; import net.minecraft.server.level.ServerBossEvent; @@ -50,7 +50,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.advancement.AdvancementProgress; -import org.spongepowered.api.advancement.AdvancementTemplate; import org.spongepowered.api.advancement.AdvancementTree; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -65,6 +64,7 @@ import org.spongepowered.api.network.ServerConnectionState; import org.spongepowered.api.network.ServerSideConnection; import org.spongepowered.api.profile.GameProfile; +import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.scoreboard.Scoreboard; import org.spongepowered.api.world.border.WorldBorder; import org.spongepowered.api.world.server.ServerWorld; @@ -106,7 +106,7 @@ public abstract class ServerPlayerMixin_API extends PlayerMixin_API implements S @Shadow @Final private PlayerAdvancements advancements; @Shadow public ServerGamePacketListenerImpl connection; - @Shadow public abstract net.minecraft.server.level.ServerLevel shadow$serverLevel(); + @Shadow public abstract net.minecraft.server.level.@NonNull ServerLevel shadow$level(); @Shadow public abstract void shadow$sendSystemMessage(final net.minecraft.network.chat.Component $$0); // @formatter:on @@ -119,7 +119,7 @@ public abstract class ServerPlayerMixin_API extends PlayerMixin_API implements S @Override public ServerWorld world() { - return (ServerWorld) this.shadow$serverLevel(); + return (ServerWorld) this.shadow$level(); } @Override @@ -242,9 +242,9 @@ public CooldownTracker cooldownTracker() { } @Override - public AdvancementProgress progress(final AdvancementTemplate advancement) { + public AdvancementProgress progress(final org.spongepowered.api.advancement.Advancement advancement) { Objects.requireNonNull(advancement, "advancement"); - final AdvancementHolder holder = new AdvancementHolder((ResourceLocation) (Object) advancement.key(), (Advancement) (Object) advancement.advancement()); + final AdvancementHolder holder = new AdvancementHolder((Identifier) (Object) advancement.key(RegistryTypes.ADVANCEMENT), (Advancement) (Object) advancement); return (AdvancementProgress) this.advancements.getOrStartProgress(holder); } @@ -289,7 +289,7 @@ public Optional setWorldBorder(final @Nullable WorldBorder border) this.connection.send(new ClientboundInitializeBorderPacket(mutableWorldBorder)); } else { // unset the border if null ((ServerPlayerBridge) this).bridge$replaceWorldBorder(null); - this.connection.send(new ClientboundInitializeBorderPacket(this.shadow$getCommandSenderWorld().getWorldBorder())); + this.connection.send(new ClientboundInitializeBorderPacket(this.shadow$level().getWorldBorder())); } return toSet; @@ -326,7 +326,7 @@ public Optional setWorldBorder(final @Nullable WorldBorder border) synchronized (this) { if (this.api$pointers == null) { this.api$pointers = pointers = Pointers.builder() - .withDynamic(Identity.NAME, () -> ((net.minecraft.server.level.ServerPlayer) (Object) this).getGameProfile().getName()) + .withDynamic(Identity.NAME, () -> ((net.minecraft.server.level.ServerPlayer) (Object) this).getGameProfile().name()) .withDynamic(Identity.DISPLAY_NAME, () -> this.displayName().get()) .withDynamic(Identity.UUID, ((Entity) (Object) this)::getUUID) .withDynamic(Identity.LOCALE, this::locale) @@ -342,6 +342,7 @@ public Optional setWorldBorder(final @Nullable WorldBorder border) @Override @Deprecated + @SuppressWarnings("deprecation") public void sendMessage(final Identity identity, final Component message, final MessageType type) { if (this.impl$isFake) { return; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/TicketTypeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/TicketTypeMixin_API.java index 19ce2393353..f4b55f7ad58 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/TicketTypeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/TicketTypeMixin_API.java @@ -36,14 +36,14 @@ public abstract class TicketTypeMixin_API implements TicketType { // @formatter:off - @Shadow @Final private String name; @Shadow @Final private long timeout; // @formatter:on @Override public String name() { - return this.name; + // TODO - change this out + return ""; } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/WorldGenRegionMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/WorldGenRegionMixin_API.java index 5f48b94a501..cd756c968a8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/WorldGenRegionMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/WorldGenRegionMixin_API.java @@ -70,7 +70,7 @@ public abstract class WorldGenRegionMixin_API implements GenerationRegion { @Inject(method = "", at = @At("RETURN")) private void api$getWorldKeyOnConstruction(final ServerLevel $$0, final StaticCache2D $$1, final ChunkStep $$2, final ChunkAccess $$3, final CallbackInfo ci) { - this.api$serverWorldKey = (ResourceKey) (Object) $$0.dimension().location(); + this.api$serverWorldKey = (ResourceKey) (Object) $$0.dimension().identifier(); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/packs/PackResourcesMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/packs/PackResourcesMixin_API.java index 7b12ef322a9..8cea7dddbad 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/packs/PackResourcesMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/packs/PackResourcesMixin_API.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.mixin.api.minecraft.server.packs; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.packs.PackResources; import net.minecraft.server.packs.resources.IoSupplier; import org.checkerframework.checker.nullness.qual.Nullable; @@ -55,7 +55,7 @@ public interface PackResourcesMixin_API extends PackContents { // @formatter:off @Shadow String shadow$packId(); - @Shadow IoSupplier shadow$getResource(net.minecraft.server.packs.PackType var1, ResourceLocation var2) throws IOException; + @Shadow IoSupplier shadow$getResource(net.minecraft.server.packs.PackType var1, Identifier var2) throws IOException; @Shadow Set shadow$getNamespaces(net.minecraft.server.packs.PackType var1); @Shadow void shadow$listResources(net.minecraft.server.packs.PackType var1, String var2, String var3, PackResources.ResourceOutput var4); @@ -75,7 +75,7 @@ default Optional resource(final PackType root, final ResourcePath path default Resource requireResource(final PackType root, final ResourcePath path) throws IOException { final Resource resource = this.api$createResource(root, path); if (resource == null) { - throw new NoSuchElementException(MessageFormat.format("Pack type {} does not contain a resource at {}", root, path)); + throw new NoSuchElementException(MessageFormat.format("Pack type {0} does not contain a resource at {1}", root, path)); } return resource; } @@ -83,7 +83,7 @@ default Resource requireResource(final PackType root, final ResourcePath path) t @Nullable default Resource api$createResource(final PackType root, final ResourcePath path) throws IOException { final net.minecraft.server.packs.PackType packType = (net.minecraft.server.packs.PackType) (Object) Objects.requireNonNull(root, "root"); - final ResourceLocation loc = (ResourceLocation) (Object) Objects.requireNonNull(path, "path").key(); + final Identifier loc = (Identifier) (Object) Objects.requireNonNull(path, "path").key(); final IoSupplier ioSupplier = this.shadow$getResource(packType, loc); return new SpongeResource(path, ioSupplier.get()); } @@ -93,7 +93,7 @@ default Collection paths(final PackType root, final String namespa Objects.requireNonNull(filter, "filter"); final net.minecraft.server.packs.PackType packType = (net.minecraft.server.packs.PackType) (Object) Objects.requireNonNull(root, "root"); - final Collection resources = new HashSet<>(); + final Collection resources = new HashSet<>(); this.shadow$listResources(packType, Objects.requireNonNull(namespace, "namespace"), Objects.requireNonNull(prefix, "prefix"), (loc, stream) -> { if (filter.test((ResourceKey) (Object) loc)) { @@ -110,7 +110,7 @@ default Collection paths(final PackType root, final String namespa default boolean exists(final PackType root, final ResourcePath path) { try { final net.minecraft.server.packs.PackType packType = (net.minecraft.server.packs.PackType) (Object) Objects.requireNonNull(root, "root"); - final ResourceLocation loc = (ResourceLocation) (Object) Objects.requireNonNull(path, "path").key(); + final Identifier loc = (Identifier) (Object) Objects.requireNonNull(path, "path").key(); final IoSupplier ioSupplier = this.shadow$getResource(packType, loc); return ioSupplier != null; } catch (IOException ignored) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerTrades_ItemListingMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/packs/resources/MultiPackResourceManagerMixin_API.java similarity index 56% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerTrades_ItemListingMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/packs/resources/MultiPackResourceManagerMixin_API.java index 9bbed6e93d7..8fd6a9e3c11 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerTrades_ItemListingMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/packs/resources/MultiPackResourceManagerMixin_API.java @@ -22,29 +22,28 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.npc; +package org.spongepowered.common.mixin.api.minecraft.server.packs.resources; -import net.minecraft.util.RandomSource; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.npc.VillagerTrades; -import net.minecraft.world.item.trading.MerchantOffer; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.item.merchant.TradeOffer; -import org.spongepowered.api.item.merchant.TradeOfferGenerator; -import org.spongepowered.api.util.RandomProvider; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.server.packs.resources.MultiPackResourceManager; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.bridge.server.packs.resources.MultiPackResourceManagerBridge; +import org.spongepowered.common.registry.RegistryHolderLogic; +import org.spongepowered.common.registry.SpongeRegistryHolder; -@Mixin(VillagerTrades.ItemListing.class) -public interface VillagerTrades_ItemListingMixin_API extends TradeOfferGenerator { +@Mixin(MultiPackResourceManager.class) +public abstract class MultiPackResourceManagerMixin_API implements SpongeRegistryHolder, MultiPackResourceManagerBridge { - // @formatter:off - @Shadow @Nullable MerchantOffer shadow$getOffer(Entity entity, RandomSource random); - // @formatter:on + private RegistryHolderLogic api$registryHolder = new RegistryHolderLogic(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); @Override - default TradeOffer apply(final org.spongepowered.api.entity.Entity merchant, final RandomProvider.Source random) { - return (TradeOffer) this.shadow$getOffer((Entity) merchant, (RandomSource) random); + public RegistryHolderLogic registryHolder() { + return this.api$registryHolder; } + @Override + public void bridge$setRegistryHolder(final RegistryHolderLogic holder) { + this.api$registryHolder = holder; + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/packs/resources/ResourceManagerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/packs/resources/ResourceManagerMixin_API.java index 90c8e37125f..f9153d508a0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/packs/resources/ResourceManagerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/packs/resources/ResourceManagerMixin_API.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.mixin.api.minecraft.server.packs.resources; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceProvider; import org.spongepowered.api.ResourceKey; @@ -49,13 +49,13 @@ public interface ResourceManagerMixin_API extends org.spongepowered.api.resource.ResourceManager { // @formatter:off - @Shadow List shadow$getResourceStack(ResourceLocation var1); - @Shadow Map shadow$listResources(String var1, Predicate var2); + @Shadow List shadow$getResourceStack(Identifier var1); + @Shadow Map shadow$listResources(String var1, Predicate var2); // @formatter:on @Override default Resource load(final ResourcePath path) throws IOException { - final ResourceLocation loc = (ResourceLocation) (Object) Objects.requireNonNull(path, "path").key(); + final Identifier loc = (Identifier) (Object) Objects.requireNonNull(path, "path").key(); final net.minecraft.server.packs.resources.Resource resource = ((ResourceProvider) this).getResourceOrThrow(loc); // TODO pass optional up to API? return new SpongeResource(resource, path); @@ -63,7 +63,7 @@ default Resource load(final ResourcePath path) throws IOException { @Override default Stream streamAll(final ResourcePath path) { - final ResourceLocation loc = (ResourceLocation) (Object) Objects.requireNonNull(path, "path").key(); + final Identifier loc = (Identifier) (Object) Objects.requireNonNull(path, "path").key(); return (Stream) (Object) this.shadow$getResourceStack(loc).stream(); } @@ -72,7 +72,7 @@ default Collection find(final String pathPrefix, final Predicate mapped = this.shadow$listResources(pathPrefix, loc -> pathFilter.test(loc.getPath())).keySet(); // TODO check filter + final Set mapped = this.shadow$listResources(pathPrefix, loc -> pathFilter.test(loc.getPath())).keySet(); // TODO check filter return mapped.stream() .map(r -> new SpongeResourcePath((ResourceKey) (Object) r)) .collect(Collectors.toList()); diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/players/GameProfileCacheMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/players/GameProfileCacheMixin_API.java index e6d3552b6ad..8c37e2c95f3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/players/GameProfileCacheMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/players/GameProfileCacheMixin_API.java @@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; +import net.minecraft.server.players.NameAndId; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.profile.GameProfile; import org.spongepowered.api.profile.GameProfileCache; @@ -50,14 +51,14 @@ import java.util.function.Predicate; import java.util.stream.Stream; -@Mixin(net.minecraft.server.players.GameProfileCache.class) +@Mixin(net.minecraft.server.players.CachedUserNameToIdResolver.class) public abstract class GameProfileCacheMixin_API implements GameProfileCache { // @formatter:off @Shadow @Final @Mutable private final Map profilesByName = new ConcurrentHashMap<>(); @Shadow @Final @Mutable private final Map profilesByUUID = new ConcurrentHashMap<>(); - @Shadow public abstract Optional shadow$get(UUID uniqueId); + @Shadow public abstract Optional shadow$get(UUID uniqueId); @Shadow protected abstract long shadow$getNextOperation(); // @formatter:on @@ -95,7 +96,7 @@ public Collection removeIf(final Predicate filter) { final Iterator it = this.profilesByUUID.values().iterator(); while (it.hasNext()) { final GameProfileCache_GameProfileInfoAccessor entry = it.next(); - final GameProfile profile = SpongeGameProfile.of(entry.invoker$getProfile()); + final GameProfile profile = SpongeGameProfile.of(entry.invoker$nameAndId()); final boolean isExpired = entry.invoker$getExpirationDate().getTime() < System.currentTimeMillis(); if (isExpired || filter.test(profile)) { it.remove(); @@ -136,9 +137,9 @@ public Optional findByName(final String name) { @Nullable GameProfileCache_GameProfileInfoAccessor entry = this.profilesByName.get(name.toLowerCase(Locale.ROOT)); if (entry != null && System.currentTimeMillis() >= entry.invoker$getExpirationDate().getTime()) { - final com.mojang.authlib.GameProfile profile = entry.invoker$getProfile(); - this.profilesByUUID.remove(profile.getId()); - this.profilesByName.remove(profile.getName().toLowerCase(Locale.ROOT)); + final var profile = entry.invoker$nameAndId(); + this.profilesByUUID.remove(profile.id()); + this.profilesByName.remove(profile.name().toLowerCase(Locale.ROOT)); entry = null; } @@ -158,7 +159,7 @@ public Map> findByNames(final Iterable nam @Override public Stream stream() { return this.profilesByName.values().stream() - .map(entry -> SpongeGameProfile.of(entry.invoker$getProfile())); + .map(entry -> SpongeGameProfile.of(entry.invoker$nameAndId())); } @Override @@ -172,8 +173,8 @@ public Collection all() { public Stream streamOfMatches(final String name) { final String search = Objects.requireNonNull(name, "name").toLowerCase(Locale.ROOT); return this.profilesByName.values().stream() - .filter(profile -> profile.invoker$getProfile().getName() != null) - .filter(profile -> profile.invoker$getProfile().getName().toLowerCase(Locale.ROOT).startsWith(search)) + .filter(profile -> profile.invoker$nameAndId().name() != null) + .filter(profile -> profile.invoker$nameAndId().name().toLowerCase(Locale.ROOT).startsWith(search)) .map(this::api$updateLastAccess); } @@ -188,6 +189,6 @@ public Collection allMatches(final String name) { return null; } entry.invoker$setLastAccess(this.shadow$getNextOperation()); - return SpongeGameProfile.of(entry.invoker$getProfile()); + return SpongeGameProfile.of(entry.invoker$nameAndId()); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/players/UserBanListEntryMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/players/UserBanListEntryMixin_API.java index 1316d762aac..5c5aa2ae390 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/players/UserBanListEntryMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/players/UserBanListEntryMixin_API.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.mixin.api.minecraft.server.players; +import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.UserBanListEntry; import org.spongepowered.api.profile.GameProfile; import org.spongepowered.api.service.ban.Ban; @@ -33,7 +34,7 @@ import org.spongepowered.common.profile.SpongeGameProfile; @Mixin(UserBanListEntry.class) -public abstract class UserBanListEntryMixin_API extends BanListEntryMixin_API implements Ban.Profile { +public abstract class UserBanListEntryMixin_API extends BanListEntryMixin_API implements Ban.Profile { @Override public BanType type() { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/sounds/MusicMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/sounds/MusicMixin_API.java index 102078cbe50..4484c81e603 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/sounds/MusicMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/sounds/MusicMixin_API.java @@ -30,14 +30,18 @@ import org.spongepowered.api.effect.sound.SoundType; import org.spongepowered.api.world.biome.ambient.SoundConfig; import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @Mixin(Music.class) +@Implements(@Interface(iface = SoundConfig.BackgroundMusic.class, prefix = "api$", remap = Interface.Remap.NONE)) public abstract class MusicMixin_API implements SoundConfig.BackgroundMusic { // @formatter:off - @Shadow @Final private Holder event; + @Shadow @Final private Holder sound; @Shadow @Final private int minDelay; @Shadow @Final private int maxDelay; @Shadow @Final private boolean replaceCurrentMusic; @@ -45,21 +49,20 @@ public abstract class MusicMixin_API implements SoundConfig.BackgroundMusic { @Override public SoundType sound() { - return (SoundType) this.event.value(); + return (SoundType) (Object) this.sound.value(); } - @Override - public int minDelay() { + @Intrinsic + public int api$minDelay() { return this.minDelay; } - @Override - public int maxDelay() { + @Intrinsic + public int api$maxDelay() { return this.maxDelay; } - @Override - public boolean replacesCurrent() { + public boolean api$replacesCurrent() { return this.replaceCurrentMusic; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/sounds/SoundEventMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/sounds/SoundEventMixin_API.java index 741f8b1de44..7d956dd57d7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/sounds/SoundEventMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/sounds/SoundEventMixin_API.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.mixin.api.minecraft.sounds; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.sounds.SoundEvent; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.effect.sound.SoundType; @@ -36,7 +36,7 @@ public abstract class SoundEventMixin_API implements SoundType { //@formatter:off - @Shadow @Final private ResourceLocation location; + @Shadow @Final private Identifier location; //@formatter:on @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/state/PropertyMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/state/PropertyMixin_API.java index 49224e9717c..b9c2e3f1044 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/state/PropertyMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/state/PropertyMixin_API.java @@ -30,6 +30,7 @@ import org.spongepowered.asm.mixin.Shadow; import java.util.Collection; +import java.util.List; import java.util.Optional; import java.util.function.Predicate; @@ -44,7 +45,7 @@ public abstract class PropertyMixin_API> implements Stat // @formatter:off @Shadow public abstract Class shadow$getValueClass(); - @Shadow public abstract Collection shadow$getPossibleValues(); + @Shadow public abstract List shadow$getPossibleValues(); @Shadow public abstract String shadow$getName(); // @formatter:on diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/tags/TagKeyMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/tags/TagKeyMixin_API.java index 073ccb77bf0..0d9ffcb60c7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/tags/TagKeyMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/tags/TagKeyMixin_API.java @@ -25,30 +25,46 @@ package org.spongepowered.common.mixin.api.minecraft.tags; import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.tags.TagKey; import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.api.tag.DefaultedTag; import org.spongepowered.api.tag.Tag; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.tag.SpongeDefaultedTag; + +import java.util.function.Supplier; @Mixin(TagKey.class) public abstract class TagKeyMixin_API implements Tag { // @formatter:off @Shadow @Final private net.minecraft.resources.ResourceKey> registry; - @Shadow @Final private ResourceLocation location; + @Shadow @Final private Identifier location; // @formatter:on @Override public RegistryType registry() { - return RegistryType.of((ResourceKey) (Object) this.registry.registry(), (ResourceKey) (Object) this.registry.location()); + return RegistryType.of((ResourceKey) (Object) this.registry.registry(), (ResourceKey) (Object) this.registry.identifier()); } @Override public ResourceKey key() { return (ResourceKey) (Object) this.location; } + + @Override + public DefaultedTag asDefaultedTag(final Supplier holder) { + return new SpongeDefaultedTag<>(this, holder); + } + + @Override + public DefaultedTag asScopedTag() { + return new SpongeDefaultedTag<>(this, SpongeCommon::scopedHolder); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/AmbientAdditionsSettingsMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/attribute/AmbientAdditionsSettingsMixin_API.java similarity index 80% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/AmbientAdditionsSettingsMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/attribute/AmbientAdditionsSettingsMixin_API.java index d7be89e3af2..efa32f2585c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/AmbientAdditionsSettingsMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/attribute/AmbientAdditionsSettingsMixin_API.java @@ -22,18 +22,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.level.biome; +package org.spongepowered.common.mixin.api.minecraft.world.attribute; import net.minecraft.core.Holder; import net.minecraft.sounds.SoundEvent; -import net.minecraft.world.level.biome.AmbientAdditionsSettings; +import net.minecraft.world.attribute.AmbientAdditionsSettings; import org.spongepowered.api.effect.sound.SoundType; import org.spongepowered.api.world.biome.ambient.SoundConfig; import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @Mixin(AmbientAdditionsSettings.class) +@Implements(@Interface(iface = SoundConfig.Additional.class, prefix = "api$")) public abstract class AmbientAdditionsSettingsMixin_API implements SoundConfig.Additional { // @formatter:off @@ -43,11 +47,11 @@ public abstract class AmbientAdditionsSettingsMixin_API implements SoundConfig.A @Override public SoundType sound() { - return (SoundType) this.soundEvent.value(); + return (SoundType) (Object) this.soundEvent.value(); } - @Override - public double tickChance() { + @Intrinsic + public double api$tickChance() { return this.tickChance; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/AmbientMoodSettingsMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/attribute/AmbientMoodSettingsMixin_API.java similarity index 79% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/AmbientMoodSettingsMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/attribute/AmbientMoodSettingsMixin_API.java index e047a72155d..8b6291acc0f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/AmbientMoodSettingsMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/attribute/AmbientMoodSettingsMixin_API.java @@ -22,18 +22,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.level.biome; +package org.spongepowered.common.mixin.api.minecraft.world.attribute; import net.minecraft.core.Holder; import net.minecraft.sounds.SoundEvent; -import net.minecraft.world.level.biome.AmbientMoodSettings; +import net.minecraft.world.attribute.AmbientMoodSettings; import org.spongepowered.api.effect.sound.SoundType; import org.spongepowered.api.world.biome.ambient.SoundConfig; import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @Mixin(AmbientMoodSettings.class) +@Implements(@Interface(iface = SoundConfig.Mood.class, prefix = "api$")) public abstract class AmbientMoodSettingsMixin_API implements SoundConfig.Mood { // @formatter:off @@ -45,21 +49,21 @@ public abstract class AmbientMoodSettingsMixin_API implements SoundConfig.Mood { @Override public SoundType sound() { - return (SoundType) this.soundEvent.value(); + return (SoundType) (Object) this.soundEvent.value(); } - @Override - public int tickDelay() { + @Intrinsic + public int api$tickDelay() { return this.tickDelay; } - @Override - public int searchRadius() { + @Intrinsic + public int api$searchRadius() { return this.blockSearchExtent; } - @Override - public double distanceModifier() { + @Intrinsic + public double api$distanceModifier() { return this.soundPositionOffset; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/AmbientParticleSettingsMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/attribute/AmbientParticleSettingsMixin_API.java similarity index 77% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/AmbientParticleSettingsMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/attribute/AmbientParticleSettingsMixin_API.java index f5cf1b58c32..48063b6e2da 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/AmbientParticleSettingsMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/attribute/AmbientParticleSettingsMixin_API.java @@ -22,34 +22,38 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.level.biome; +package org.spongepowered.common.mixin.api.minecraft.world.attribute; -import net.minecraft.world.level.biome.AmbientParticleSettings; +import net.minecraft.world.attribute.AmbientParticle; import org.spongepowered.api.effect.particle.ParticleType; import org.spongepowered.api.world.biome.ambient.ParticleConfig; import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -@Mixin(AmbientParticleSettings.class) +@Mixin(AmbientParticle.class) +@Implements(@Interface(iface = ParticleConfig.class, prefix = "api$")) public abstract class AmbientParticleSettingsMixin_API implements ParticleConfig { // @formatter:off @Shadow @Final private float probability; - @Shadow @Final private net.minecraft.core.particles.ParticleOptions options; + @Shadow @Final private net.minecraft.core.particles.ParticleOptions particle; // @formatter:on - @Override - public float probability() { + @Intrinsic + public float api$probability() { return this.probability; } @Override public ParticleType type() { - if (this.options instanceof ParticleType type) { + if (this.particle instanceof ParticleType type) { return type; } - throw new UnsupportedOperationException("Unsupported ParticleOptions: " + this.options.getClass().getSimpleName()); + throw new UnsupportedOperationException("Unsupported ParticleOptions: " + this.particle.getClass().getSimpleName()); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/damagesource/DamageTypeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/damagesource/DamageTypeMixin_API.java index 01af5bb9ca2..c90ee60f58d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/damagesource/DamageTypeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/damagesource/DamageTypeMixin_API.java @@ -25,19 +25,17 @@ package org.spongepowered.common.mixin.api.minecraft.world.damagesource; import net.minecraft.world.damagesource.DamageEffects; +import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.event.cause.entity.damage.DamageEffect; import org.spongepowered.api.event.cause.entity.damage.DamageScaling; import org.spongepowered.api.event.cause.entity.damage.DamageType; -import org.spongepowered.api.registry.DefaultedRegistryType; -import org.spongepowered.api.registry.RegistryTypes; -import org.spongepowered.api.tag.Tag; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.util.DataPackUtil; -import java.util.Collection; -import java.util.Set; -import java.util.stream.Collectors; +import java.util.Optional; @Mixin(value = net.minecraft.world.damagesource.DamageType.class) public abstract class DamageTypeMixin_API implements DamageType { @@ -72,18 +70,7 @@ public DamageEffect effect() { } @Override - public boolean is(final Tag tag) { - final Set matchingTypes = RegistryTypes.DAMAGE_TYPE.get().taggedValues(tag); - return matchingTypes.contains(this); - } - - @Override - public DefaultedRegistryType registryType() { - return RegistryTypes.DAMAGE_TYPE; - } - - @Override - public Collection> tags() { - return this.registryType().get().tags().filter(this::is).collect(Collectors.toSet()); + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, net.minecraft.world.damagesource.DamageType.DIRECT_CODEC, (net.minecraft.world.damagesource.DamageType) (Object) this); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/effect/MobEffectMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/effect/MobEffectMixin_API.java index 11c63c2219c..7a0cd508b9b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/effect/MobEffectMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/effect/MobEffectMixin_API.java @@ -39,12 +39,12 @@ public abstract class MobEffectMixin_API implements PotionEffectType { // @formatter:off - @Shadow public abstract boolean shadow$isInstantenous(); + @Shadow public abstract boolean shadow$isInstantaneous(); @Shadow public abstract net.minecraft.network.chat.Component shadow$getDisplayName(); // @formatter:on public boolean potionEffectType$isInstant() { - return this.shadow$isInstantenous(); + return this.shadow$isInstantaneous(); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java index a7473ebe639..74a8157649d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityMixin_API.java @@ -30,15 +30,20 @@ import net.minecraft.commands.arguments.EntityAnchorArgument; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.SynchedEntityData; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; +import net.minecraft.resources.Identifier; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.RelativeMovement; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.PositionMoveRotation; +import net.minecraft.world.entity.Relative; import net.minecraft.world.level.Level; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.phys.Vec3; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; @@ -63,6 +68,7 @@ import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.world.entity.EntityBridge; +import org.spongepowered.common.data.persistence.NBTTranslator; import org.spongepowered.common.entity.SpongeEntityArchetypeBuilder; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.util.Constants; @@ -82,6 +88,7 @@ public abstract class EntityMixin_API implements org.spongepowered.api.entity.Entity { // @formatter:off + @Shadow private Vec3 position; @Shadow private float yRot; @Shadow private float xRot; @Shadow @Final protected net.minecraft.util.RandomSource random; @@ -93,18 +100,17 @@ public abstract class EntityMixin_API implements org.spongepowered.api.entity.En @Shadow public abstract double shadow$getX(); @Shadow public abstract double shadow$getY(); @Shadow public abstract double shadow$getZ(); - @Shadow public abstract Level shadow$getCommandSenderWorld(); - @Shadow @Nullable public abstract MinecraftServer shadow$getServer(); @Shadow public abstract boolean shadow$isRemoved(); @Shadow public abstract UUID shadow$getUUID(); - @Shadow public abstract boolean shadow$hurt(DamageSource source, float amount); + @Shadow public abstract boolean shadow$hurtOrSimulate(DamageSource source, float amount); @Shadow protected abstract void shadow$setRot(float yaw, float pitch); @Shadow public abstract net.minecraft.world.phys.AABB shadow$getBoundingBox(); @Shadow public abstract void shadow$setRemoved(Entity.RemovalReason var1); @Shadow public abstract void shadow$discard(); @Shadow public abstract void shadow$lookAt(EntityAnchorArgument.Anchor param0, Vec3 param1); - @Shadow public abstract CompoundTag shadow$saveWithoutId(CompoundTag $$0); + @Shadow public abstract void shadow$saveWithoutId(ValueOutput $$0); @Shadow public abstract Level shadow$level(); + @Shadow public abstract Vec3 shadow$position(); // @formatter:on @Override @@ -129,7 +135,7 @@ public boolean setPosition(final Vector3d position) { @Override public ServerLocation location() { - return ServerLocation.of((org.spongepowered.api.world.server.ServerWorld) this.shadow$getCommandSenderWorld(), this.position()); + return ServerLocation.of((org.spongepowered.api.world.server.ServerWorld) this.shadow$level(), this.position()); } @Override @@ -163,7 +169,7 @@ public Transform transform() { @Override public boolean setTransform(final Transform transform) { - if (!PhaseTracker.SERVER.onSidedThread()) { + if (!PhaseTracker.getWorldInstance((ServerLevel) this.shadow$level()).onSidedThread()) { return false; } @@ -198,8 +204,7 @@ public void setRotation(final Vector3d rotation) { } if (((Entity) (Object) this) instanceof ServerPlayer && ((ServerPlayer) (Object) this).connection != null) { // Force an update, this also set the rotation in this entity - ((ServerPlayer) (Object) this).connection.teleport(this.position().x(), this.position().y(), - this.position().z(), (float) rotation.y(), (float) rotation.x(), EnumSet.noneOf(RelativeMovement.class)); + ((ServerPlayer) (Object) this).connection.teleport(new PositionMoveRotation(this.position, Vec3.ZERO, (float) rotation.y(), (float) rotation.x()), EnumSet.noneOf(Relative.class)); } else { // Let the entity tracker do its job, this just updates the variables this.shadow$setRot((float) rotation.y(), (float) rotation.x()); @@ -236,6 +241,8 @@ public boolean isLoaded() { this.shadow$setRemoved(Entity.RemovalReason.DISCARDED); } + + @Override public boolean damage(final double damage, final org.spongepowered.api.event.cause.entity.damage.source.DamageSource damageSource) { if (!(damageSource instanceof DamageSource)) { @@ -243,7 +250,7 @@ public boolean damage(final double damage, final org.spongepowered.api.event.cau return false; } // Causes at this point should already be pushed from plugins before this point with the cause system. - return this.shadow$hurt((DamageSource) damageSource, (float) damage); + return this.shadow$hurtOrSimulate((DamageSource) damageSource, (float) damage); } @Override @@ -263,10 +270,10 @@ public int contentVersion() { @Override public DataContainer toContainer() { - final Registry> entityTypeRegistry = SpongeCommon.vanillaRegistry(Registries.ENTITY_TYPE); - final CompoundTag compound = new CompoundTag(); - compound.putString("id", entityTypeRegistry.getKey((net.minecraft.world.entity.EntityType) this.type()).toString()); - this.shadow$saveWithoutId(compound); + final var output = TagValueOutput.createWithContext(ProblemReporter.DISCARDING, this.level.registryAccess()); + output.store("id", net.minecraft.world.entity.EntityType.CODEC, this.type); + this.shadow$saveWithoutId(output); + final DataContainer entityData = NBTTranslator.INSTANCE.translateFrom(output.buildResult()); final DataContainer container = DataContainer.createNew() .set(Queries.CONTENT_VERSION, this.contentVersion()) .set(Queries.WORLD_KEY, ((org.spongepowered.api.world.server.ServerWorld) this.world()).key().formatted()) @@ -285,8 +292,8 @@ public DataContainer toContainer() { .set(Queries.POSITION_Y, this.scale().y()) .set(Queries.POSITION_Z, this.scale().z()) .container() - .set(Constants.Entity.V2.DATA_VERSION, SharedConstants.getCurrentVersion().getDataVersion().getVersion()) - .set(Constants.Entity.V2.DATA, compound); + .set(Constants.Entity.V2.DATA_VERSION, SharedConstants.getCurrentVersion().dataVersion().version()) + .set(Constants.Entity.V2.DATA, entityData); return container; } @@ -297,13 +304,15 @@ public org.spongepowered.api.entity.Entity copy() { } try { final Registry> entityTypeRegistry = SpongeCommon.vanillaRegistry(Registries.ENTITY_TYPE); - final CompoundTag compound = new CompoundTag(); - compound.putString("id", entityTypeRegistry.getKey((net.minecraft.world.entity.EntityType) this.type()).toString()); - this.shadow$saveWithoutId(compound); - final Entity entity = net.minecraft.world.entity.EntityType.loadEntityRecursive(compound, this.shadow$getCommandSenderWorld(), (createdEntity) -> { - createdEntity.setUUID(UUID.randomUUID()); - return createdEntity; - }); + final var output = TagValueOutput.createWithContext(ProblemReporter.DISCARDING, this.level.registryAccess()); + output.putString("id", entityTypeRegistry.getKey((net.minecraft.world.entity.EntityType) this.type()).toString()); + this.shadow$saveWithoutId(output); + final var input = TagValueInput.create(ProblemReporter.DISCARDING, this.level.registryAccess(), output.buildResult()); + final @Nullable Entity entity = net.minecraft.world.entity.EntityType.loadEntityRecursive(input, this.shadow$level(), EntitySpawnReason.COMMAND, + (createdEntity) -> { + createdEntity.setUUID(UUID.randomUUID()); + return createdEntity; + }); return (org.spongepowered.api.entity.Entity) entity; } catch (final Exception e) { throw new IllegalArgumentException("Could not copy the entity:", e); @@ -341,8 +350,8 @@ public EntityArchetype createArchetype() { @Override public HoverEvent asHoverEvent(final UnaryOperator op) { final Registry> entityTypeRegistry = SpongeCommon.vanillaRegistry(Registries.ENTITY_TYPE); - final ResourceLocation entityTypeKey = entityTypeRegistry.getKey((net.minecraft.world.entity.EntityType) this.type()); - return HoverEvent.showEntity(op.apply(HoverEvent.ShowEntity.of((Key) (Object) entityTypeKey, this.uniqueId(), this.displayName().get()))); + final Identifier entityTypeKey = entityTypeRegistry.getKey((net.minecraft.world.entity.EntityType) this.type()); + return HoverEvent.showEntity(op.apply(HoverEvent.ShowEntity.showEntity((Key) (Object) entityTypeKey, this.uniqueId(), this.displayName().get()))); } protected Set> api$getVanillaValues() { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityTypeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityTypeMixin_API.java index efa78182d6f..391067fbb49 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityTypeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/EntityTypeMixin_API.java @@ -26,21 +26,15 @@ import net.kyori.adventure.text.Component; import net.minecraft.core.Holder; -import net.minecraft.tags.TagKey; import net.minecraft.world.entity.MobCategory; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.EntityCategory; import org.spongepowered.api.entity.EntityType; -import org.spongepowered.api.registry.DefaultedRegistryType; -import org.spongepowered.api.registry.RegistryTypes; -import org.spongepowered.api.tag.Tag; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.adventure.SpongeAdventure; -import java.util.Collection; -import java.util.stream.Collectors; @Mixin(net.minecraft.world.entity.EntityType.class) public abstract class EntityTypeMixin_API implements EntityType { @@ -81,21 +75,6 @@ public boolean isSummonable() { return this.shadow$canSummon(); } - @Override - public DefaultedRegistryType> registryType() { - return RegistryTypes.ENTITY_TYPE; - } - - @Override - public Collection>> tags() { - return this.registryType().get().tags().filter(this::is).collect(Collectors.toSet()); - } - - @Override - public boolean is(Tag> tag) { - return this.builtInRegistryHolder.is((TagKey>) (Object) tag); - } - @SuppressWarnings("ConstantConditions") @Override public EntityCategory category() { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/HumanoidArmMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/HumanoidArmMixin_API.java index 3f60eab0e26..2c06dd309bb 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/HumanoidArmMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/HumanoidArmMixin_API.java @@ -30,16 +30,17 @@ import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.adventure.SpongeAdventure; @Mixin(HumanoidArm.class) public abstract class HumanoidArmMixin_API implements HandPreference { // @formatter:off - @Shadow @Final private String translationKey; + @Shadow @Final private net.minecraft.network.chat.Component caption; // @formatter:on @Override public Component asComponent() { - return Component.translatable(this.translationKey); + return SpongeAdventure.asAdventure(this.caption); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/SaddleableMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/SaddleableMixin_API.java index 76883707f1e..78599dd577f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/SaddleableMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/SaddleableMixin_API.java @@ -24,9 +24,14 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.entity; -import org.spongepowered.api.entity.Saddleable; +import net.minecraft.world.entity.ItemSteerable; +import net.minecraft.world.entity.animal.equine.AbstractHorse; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.Saddleable.class) -public interface SaddleableMixin_API extends Saddleable { +// TODO - the interface is gone, but the premise of being able to use the saddle +// still exists. +@SuppressWarnings("removal") +@Mixin({ItemSteerable.class, AbstractHorse.class}) +public interface SaddleableMixin_API extends org.spongepowered.api.entity.Saddleable { + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/attributes/AttributeInstanceMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/attributes/AttributeInstanceMixin_API.java index 214bcf05b37..92c9e8c6c86 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/attributes/AttributeInstanceMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/attributes/AttributeInstanceMixin_API.java @@ -25,7 +25,7 @@ package org.spongepowered.common.mixin.api.minecraft.world.entity.ai.attributes; import net.minecraft.core.Holder; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.entity.ai.attributes.AttributeInstance; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.entity.attribute.Attribute; @@ -59,11 +59,11 @@ public abstract class AttributeInstanceMixin_API implements Attribute { @Shadow public abstract double shadow$getValue(); @Shadow protected abstract void shadow$addModifier(net.minecraft.world.entity.ai.attributes.AttributeModifier modifier); @Shadow public abstract void shadow$removeModifier(net.minecraft.world.entity.ai.attributes.AttributeModifier modifier); - @Shadow public abstract boolean shadow$removeModifier(ResourceLocation $$0); + @Shadow public abstract boolean shadow$removeModifier(Identifier $$0); @Shadow abstract Map shadow$getModifiers(net.minecraft.world.entity.ai.attributes.AttributeModifier.Operation p_225504_1_); @Shadow public abstract Set shadow$getModifiers(); - @Shadow public abstract boolean shadow$hasModifier(final ResourceLocation $$0); - @Shadow @Nullable public abstract net.minecraft.world.entity.ai.attributes.AttributeModifier shadow$getModifier(final ResourceLocation $$0); + @Shadow public abstract boolean shadow$hasModifier(final Identifier $$0); + @Shadow @Nullable public abstract net.minecraft.world.entity.ai.attributes.AttributeModifier shadow$getModifier(final Identifier $$0); // @formatter:on @@ -99,7 +99,7 @@ public Collection modifiers(final AttributeOperation operatio @Override public boolean hasModifier(final AttributeModifier modifier) { - return this.shadow$hasModifier((ResourceLocation) (Object) modifier.key()); + return this.shadow$hasModifier((Identifier) (Object) modifier.key()); } @Override @@ -114,12 +114,12 @@ public void removeModifier(final AttributeModifier modifier) { @Override public void removeModifier(ResourceKey key) { - this.shadow$removeModifier((ResourceLocation) (Object) key); + this.shadow$removeModifier((Identifier) (Object) key); } @Override public Optional modifier(final ResourceKey key) { - return Optional.ofNullable((AttributeModifier) (Object) this.shadow$getModifier((ResourceLocation) (Object) Objects.requireNonNull(key, "uniqueId"))); + return Optional.ofNullable((AttributeModifier) (Object) this.shadow$getModifier((Identifier) (Object) Objects.requireNonNull(key, "uniqueId"))); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/attributes/AttributeModifierMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/attributes/AttributeModifierMixin_API.java index 766744c19ee..5eda287243c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/attributes/AttributeModifierMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/attributes/AttributeModifierMixin_API.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.entity.ai.attributes; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.entity.ai.attributes.AttributeModifier.Operation; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.entity.attribute.AttributeModifier; @@ -39,7 +39,7 @@ public abstract class AttributeModifierMixin_API implements AttributeModifier { // @formatter:off @Shadow @Final private double amount; @Shadow @Final private Operation operation; - @Shadow @Final private ResourceLocation id; + @Shadow @Final private Identifier id; // @formatter:on diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/goal/AvoidEntityGoalMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/goal/AvoidEntityGoalMixin_API.java index 6b54348d93e..48acc27bbe2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/goal/AvoidEntityGoalMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/goal/AvoidEntityGoalMixin_API.java @@ -24,24 +24,24 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.entity.ai.goal; -import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.targeting.TargetingConditions; import org.spongepowered.api.entity.ai.goal.builtin.creature.AvoidLivingGoal; import org.spongepowered.api.entity.living.Living; import org.spongepowered.api.entity.living.PathfinderAgent; +import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.accessor.world.entity.ai.targeting.TargetingConditionsAccessor; -import java.util.function.Predicate; +import java.util.function.BiPredicate; @SuppressWarnings({"unchecked"}) @Mixin(net.minecraft.world.entity.ai.goal.AvoidEntityGoal.class) public abstract class AvoidEntityGoalMixin_API extends GoalMixin_API implements AvoidLivingGoal { - private static final Predicate ALWAYS_TRUE = e -> true; + private static final BiPredicate ALWAYS_TRUE = (e, l) -> true; // @formatter:off @Shadow @Final @Mutable private double walkSpeedModifier; @@ -51,14 +51,14 @@ public abstract class AvoidEntityGoalMixin_API extends GoalMixin_API targetSelector() { - final Predicate predicate = ((TargetingConditionsAccessor) this.avoidEntityTargeting).accessor$selector(); - return (Predicate) (Object) (predicate == null ? AvoidEntityGoalMixin_API.ALWAYS_TRUE : predicate); + public BiPredicate targetSelector() { + final TargetingConditions.Selector predicate = ((TargetingConditionsAccessor) this.avoidEntityTargeting).accessor$selector(); + return (BiPredicate) (Object) (predicate == null ? AvoidEntityGoalMixin_API.ALWAYS_TRUE : predicate); } @Override - public AvoidLivingGoal setTargetSelector(Predicate predicate) { - this.avoidEntityTargeting.selector((Predicate) (Object) predicate); + public AvoidLivingGoal setTargetSelector(BiPredicate predicate) { + this.avoidEntityTargeting.selector((TargetingConditions.Selector) predicate); return this; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoalMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoalMixin_API.java index ed9aa85e4b6..00f45740062 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoalMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoalMixin_API.java @@ -74,13 +74,13 @@ public FindNearestAttackableTargetGoal setChance(int chance) { @Override public FindNearestAttackableTargetGoal filter(Predicate predicate) { - this.targetConditions.selector(((Predicate) (Object) predicate)); + this.targetConditions.selector((e, l) -> predicate.test((Living) l)); return this; } @Override public Predicate filter() { - final Predicate predicate = ((TargetingConditionsAccessor) this.targetConditions).accessor$selector(); + final TargetingConditions.Selector predicate = ((TargetingConditionsAccessor) this.targetConditions).accessor$selector(); return (Predicate) (Object) (predicate == null ? NearestAttackableTargetGoalMixin_API.ALWAYS_TRUE : predicate); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AgeableWaterCreatureMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AgeableWaterCreatureMixin_API.java new file mode 100644 index 00000000000..cb0730af804 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AgeableWaterCreatureMixin_API.java @@ -0,0 +1,36 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; + +import net.minecraft.world.entity.animal.AgeableWaterCreature; +import org.spongepowered.api.entity.Ageable; +import org.spongepowered.api.entity.living.aquatic.Aquatic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.MobMixin_API; + +@Mixin(AgeableWaterCreature.class) +public abstract class AgeableWaterCreatureMixin_API extends MobMixin_API implements Aquatic, Ageable { + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/axolotl/AxolotlVariantMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/axolotl/AxolotlVariantMixin_API.java new file mode 100644 index 00000000000..1724094625c --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/axolotl/AxolotlVariantMixin_API.java @@ -0,0 +1,34 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.axolotl; + +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import org.spongepowered.api.data.type.AxolotlVariant; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(Axolotl.Variant.class) +public abstract class AxolotlVariantMixin_API implements AxolotlVariant { + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/BeeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/bee/BeeMixin_API.java similarity index 90% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/BeeMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/bee/BeeMixin_API.java index 6240a3fb372..1ba919e52be 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/BeeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/bee/BeeMixin_API.java @@ -22,12 +22,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.bee; import org.spongepowered.api.entity.living.animal.Bee; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AnimalMixin_API; -@Mixin(net.minecraft.world.entity.animal.Bee.class) +@Mixin(net.minecraft.world.entity.animal.bee.Bee.class) public abstract class BeeMixin_API extends AnimalMixin_API implements Bee { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/camel/CamelHuskMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/camel/CamelHuskMixin_API.java new file mode 100644 index 00000000000..b40ef8a9156 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/camel/CamelHuskMixin_API.java @@ -0,0 +1,33 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.camel; + +import net.minecraft.world.entity.animal.camel.CamelHusk; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(CamelHusk.class) +public abstract class CamelHuskMixin_API extends CamelMixin_API implements org.spongepowered.api.entity.living.animal.camel.CamelHusk { + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/camel/CamelMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/camel/CamelMixin_API.java index 4ff0a35515b..a4d4a4ddfb2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/camel/CamelMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/camel/CamelMixin_API.java @@ -26,9 +26,9 @@ import net.minecraft.world.entity.animal.camel.Camel; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse.AbstractHorseMixin_API; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine.AbstractHorseMixin_API; @Mixin(Camel.class) -public abstract class CamelMixin_API extends AbstractHorseMixin_API implements org.spongepowered.api.entity.living.animal.horse.Camel { +public abstract class CamelMixin_API extends AbstractHorseMixin_API implements org.spongepowered.api.entity.living.animal.camel.Camel { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/ChickenMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/chicken/ChickenMixin_API.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/ChickenMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/chicken/ChickenMixin_API.java index f118e88bf8c..6c6d746ded8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/ChickenMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/chicken/ChickenMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.chicken; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.Chicken; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AnimalMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Chicken.class) +@Mixin(net.minecraft.world.entity.animal.chicken.Chicken.class) public abstract class ChickenMixin_API extends AnimalMixin_API implements Chicken { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CowMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/chicken/ChickenVariantMixin_API.java similarity index 86% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CowMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/chicken/ChickenVariantMixin_API.java index bec6947e2db..22f67fd08dd 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CowMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/chicken/ChickenVariantMixin_API.java @@ -22,12 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.chicken; -import org.spongepowered.api.entity.living.animal.cow.Cow; +import net.minecraft.world.entity.animal.chicken.ChickenVariant; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.Cow.class) -public abstract class CowMixin_API extends AnimalMixin_API implements Cow { - +@Mixin(ChickenVariant.class) +public abstract class ChickenVariantMixin_API implements org.spongepowered.api.data.type.ChickenVariant { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/AbstractCowMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/AbstractCowMixin_API.java new file mode 100644 index 00000000000..5177b50a11e --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/AbstractCowMixin_API.java @@ -0,0 +1,34 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.cow; + +import org.spongepowered.api.entity.living.animal.cow.Cow; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AnimalMixin_API; + +@Mixin(net.minecraft.world.entity.animal.cow.AbstractCow.class) +public abstract class AbstractCowMixin_API extends AnimalMixin_API implements Cow { + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/PowerableMobMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/CowVariantMixin_API.java similarity index 87% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/PowerableMobMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/CowVariantMixin_API.java index 47588fb4481..bd48197db3a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/PowerableMobMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/CowVariantMixin_API.java @@ -22,12 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.cow; -import net.minecraft.world.entity.PowerableMob; -import org.spongepowered.api.entity.Chargeable; +import net.minecraft.world.entity.animal.cow.CowVariant; import org.spongepowered.asm.mixin.Mixin; -@Mixin(PowerableMob.class) -public interface PowerableMobMixin_API extends Chargeable { +@Mixin(CowVariant.class) +public abstract class CowVariantMixin_API implements org.spongepowered.api.data.type.CowVariant { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/MushroomCowMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/MushroomCowMixin_API.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/MushroomCowMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/MushroomCowMixin_API.java index 41fe27e820a..7acf88dae03 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/MushroomCowMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/MushroomCowMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.cow; -import net.minecraft.world.entity.animal.MushroomCow; +import net.minecraft.world.entity.animal.cow.MushroomCow; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.cow.Mooshroom; @@ -33,7 +33,7 @@ import java.util.Set; @Mixin(MushroomCow.class) -public abstract class MushroomCowMixin_API extends CowMixin_API implements Mooshroom { +public abstract class MushroomCowMixin_API extends AbstractCowMixin_API implements Mooshroom { @Override protected Set> api$getVanillaValues() { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/MushroomCow_MushroomTypeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/MushroomCow_MushroomTypeMixin_API.java similarity index 93% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/MushroomCow_MushroomTypeMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/MushroomCow_MushroomTypeMixin_API.java index 87d51f488d7..7cc2ed7cd13 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/MushroomCow_MushroomTypeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/cow/MushroomCow_MushroomTypeMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.cow; -import net.minecraft.world.entity.animal.MushroomCow; +import net.minecraft.world.entity.animal.cow.MushroomCow; import org.spongepowered.api.data.type.MooshroomType; import org.spongepowered.asm.mixin.Mixin; -@Mixin(MushroomCow.MushroomType.class) +@Mixin(MushroomCow.Variant.class) public abstract class MushroomCow_MushroomTypeMixin_API implements MooshroomType { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/DolphinMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/dolphin/DolphinMixin_API.java similarity index 86% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/DolphinMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/dolphin/DolphinMixin_API.java index fb794fbe8c7..913c2e43aad 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/DolphinMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/dolphin/DolphinMixin_API.java @@ -22,17 +22,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.dolphin; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.aquatic.Dolphin; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AgeableWaterCreatureMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Dolphin.class) -public abstract class DolphinMixin_API extends WaterAnimalMixin_API implements Dolphin { +@Mixin(net.minecraft.world.entity.animal.dolphin.Dolphin.class) +public abstract class DolphinMixin_API extends AgeableWaterCreatureMixin_API implements Dolphin { @Override protected Set> api$getVanillaValues() { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/AbstractChestedHorseMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/AbstractChestedHorseMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/AbstractChestedHorseMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/AbstractChestedHorseMixin_API.java index bcfa475021b..d01a3c75354 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/AbstractChestedHorseMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/AbstractChestedHorseMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; -import net.minecraft.world.entity.animal.horse.AbstractChestedHorse; +import net.minecraft.world.entity.animal.equine.AbstractChestedHorse; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.horse.PackHorse; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/AbstractHorseMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/AbstractHorseMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/AbstractHorseMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/AbstractHorseMixin_API.java index 40361de0a52..928067e27ac 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/AbstractHorseMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/AbstractHorseMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; -import net.minecraft.world.entity.animal.horse.AbstractHorse; +import net.minecraft.world.entity.animal.equine.AbstractHorse; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.horse.HorseLike; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/DonkeyMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/DonkeyMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/DonkeyMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/DonkeyMixin_API.java index 91ea7ac9fde..dcc5844e28c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/DonkeyMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/DonkeyMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; import org.spongepowered.api.entity.living.animal.horse.Donkey; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.horse.Donkey.class) +@Mixin(net.minecraft.world.entity.animal.equine.Donkey.class) public abstract class DonkeyMixin_API extends AbstractChestedHorseMixin_API implements Donkey { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/HorseMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/HorseMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/HorseMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/HorseMixin_API.java index a2c8b02512b..a0f8c98e931 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/HorseMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/HorseMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.horse.Horse.class) +@Mixin(net.minecraft.world.entity.animal.equine.Horse.class) public abstract class HorseMixin_API extends AbstractHorseMixin_API implements Horse { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/LLama_VariantMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/LLama_VariantMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/LLama_VariantMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/LLama_VariantMixin_API.java index 4eecb60c0db..358d10d77d1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/LLama_VariantMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/LLama_VariantMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; import org.spongepowered.api.data.type.LlamaType; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.horse.Llama.Variant.class) +@Mixin(net.minecraft.world.entity.animal.equine.Llama.Variant.class) public abstract class LLama_VariantMixin_API implements LlamaType { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/LlamaMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/LlamaMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/LlamaMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/LlamaMixin_API.java index 8d4a0ad8d70..23043e38db9 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/LlamaMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/LlamaMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.horse.Llama.class) +@Mixin(net.minecraft.world.entity.animal.equine.Llama.class) public abstract class LlamaMixin_API extends AbstractChestedHorseMixin_API implements Llama { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/MarkingsMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/MarkingsMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/MarkingsMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/MarkingsMixin_API.java index 94b206d3e46..34d0c42a4d4 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/MarkingsMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/MarkingsMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; import org.spongepowered.api.data.type.HorseStyle; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.horse.Markings.class) +@Mixin(net.minecraft.world.entity.animal.equine.Markings.class) public abstract class MarkingsMixin_API implements HorseStyle { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/MuleMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/MuleMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/MuleMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/MuleMixin_API.java index 1e353d79fa3..a9b64f76717 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/MuleMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/MuleMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; import org.spongepowered.api.entity.living.animal.horse.Mule; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.horse.Mule.class) +@Mixin(net.minecraft.world.entity.animal.equine.Mule.class) public abstract class MuleMixin_API extends AbstractChestedHorseMixin_API implements Mule { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/SkeletonHorseMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/SkeletonHorseMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/SkeletonHorseMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/SkeletonHorseMixin_API.java index 200470d74e1..ae45c84d937 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/SkeletonHorseMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/SkeletonHorseMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; import org.spongepowered.api.entity.living.animal.horse.SkeletonHorse; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.horse.SkeletonHorse.class) +@Mixin(net.minecraft.world.entity.animal.equine.SkeletonHorse.class) public abstract class SkeletonHorseMixin_API extends AbstractHorseMixin_API implements SkeletonHorse { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/TraderLlamaMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/TraderLlamaMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/TraderLlamaMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/TraderLlamaMixin_API.java index a9b6f1326bd..bfe88eb7699 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/TraderLlamaMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/TraderLlamaMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.horse.TraderLlama.class) +@Mixin(net.minecraft.world.entity.animal.equine.TraderLlama.class) public abstract class TraderLlamaMixin_API extends LlamaMixin_API implements TraderLlama { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/VariantMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/VariantMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/VariantMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/VariantMixin_API.java index 5b2000ff06b..485c8aad0cd 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/VariantMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/VariantMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; import org.spongepowered.api.data.type.HorseColor; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.horse.Variant.class) +@Mixin(net.minecraft.world.entity.animal.equine.Variant.class) public abstract class VariantMixin_API implements HorseColor { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/ZombieHorseMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/ZombieHorseMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/ZombieHorseMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/ZombieHorseMixin_API.java index f0a45b55f61..a0d97e58088 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/horse/ZombieHorseMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/equine/ZombieHorseMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.horse; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.equine; import org.spongepowered.api.entity.living.animal.horse.ZombieHorse; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.horse.ZombieHorse.class) +@Mixin(net.minecraft.world.entity.animal.equine.ZombieHorse.class) public abstract class ZombieHorseMixin_API extends AbstractHorseMixin_API implements ZombieHorse { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CatMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/feline/CatMixin_API.java similarity index 97% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CatMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/feline/CatMixin_API.java index d2b9f0e2f6e..49a7468a23f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CatMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/feline/CatMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.feline; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -32,7 +32,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Cat.class) +@Mixin(net.minecraft.world.entity.animal.feline.Cat.class) public abstract class CatMixin_API extends TamableAnimalMixin_API implements Cat { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CatVariantMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/feline/CatVariantMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CatVariantMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/feline/CatVariantMixin_API.java index 35500e6c8f6..cc34f8928c5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CatVariantMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/feline/CatVariantMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.feline; -import net.minecraft.world.entity.animal.CatVariant; +import net.minecraft.world.entity.animal.feline.CatVariant; import org.spongepowered.api.data.type.CatType; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/OcelotMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/feline/OcelotMixin_API.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/OcelotMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/feline/OcelotMixin_API.java index ec8c142d6d5..67ee26ea5b9 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/OcelotMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/feline/OcelotMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.feline; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.Ocelot; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AnimalMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Ocelot.class) +@Mixin(net.minecraft.world.entity.animal.feline.Ocelot.class) public abstract class OcelotMixin_API extends AnimalMixin_API implements Ocelot { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AbstractFishMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/AbstractFishMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AbstractFishMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/AbstractFishMixin_API.java index be40d35ffc3..dd07e93ece7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AbstractFishMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/AbstractFishMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fish; -import net.minecraft.world.entity.animal.AbstractFish; +import net.minecraft.world.entity.animal.fish.AbstractFish; import org.spongepowered.api.entity.living.aquatic.fish.Fish; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AbstractSchoolingFishMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/AbstractSchoolingFishMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AbstractSchoolingFishMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/AbstractSchoolingFishMixin_API.java index bbb9cfd9779..93a63da464e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AbstractSchoolingFishMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/AbstractSchoolingFishMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fish; -import net.minecraft.world.entity.animal.AbstractSchoolingFish; +import net.minecraft.world.entity.animal.fish.AbstractSchoolingFish; import org.spongepowered.api.entity.living.aquatic.fish.school.SchoolingFish; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CodMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/CodMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CodMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/CodMixin_API.java index 5861ec74078..543e8b13f6c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/CodMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/CodMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fish; import org.spongepowered.api.entity.living.aquatic.fish.school.Cod; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.Cod.class) +@Mixin(net.minecraft.world.entity.animal.fish.Cod.class) public abstract class CodMixin_API extends AbstractSchoolingFishMixin_API implements Cod { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PufferfishMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/PufferfishMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PufferfishMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/PufferfishMixin_API.java index 08db79bbddb..584d634143c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PufferfishMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/PufferfishMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fish; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Pufferfish.class) +@Mixin(net.minecraft.world.entity.animal.fish.Pufferfish.class) public abstract class PufferfishMixin_API extends AbstractFishMixin_API implements Pufferfish { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SalmonMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/SalmonMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SalmonMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/SalmonMixin_API.java index 77c0cbd43b2..a42d21c43bc 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SalmonMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/SalmonMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fish; import org.spongepowered.api.entity.living.aquatic.fish.school.Salmon; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.Salmon.class) +@Mixin(net.minecraft.world.entity.animal.fish.Salmon.class) public abstract class SalmonMixin_API extends AbstractSchoolingFishMixin_API implements Salmon { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/SalmonVariantMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/SalmonVariantMixin_API.java new file mode 100644 index 00000000000..605c6e1b15f --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/SalmonVariantMixin_API.java @@ -0,0 +1,34 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fish; + +import net.minecraft.world.entity.animal.fish.Salmon; +import org.spongepowered.api.data.type.SalmonSize; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(Salmon.Variant.class) +public abstract class SalmonVariantMixin_API implements SalmonSize { + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/TropicalFishMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/TropicalFishMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/TropicalFishMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/TropicalFishMixin_API.java index c899e8468dc..eaba0d4a2d7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/TropicalFishMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/TropicalFishMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fish; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.TropicalFish.class) +@Mixin(net.minecraft.world.entity.animal.fish.TropicalFish.class) public abstract class TropicalFishMixin_API extends AbstractSchoolingFishMixin_API implements TropicalFish { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/TropicalFish_PatternMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/TropicalFish_PatternMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/TropicalFish_PatternMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/TropicalFish_PatternMixin_API.java index f4fe5383e8c..1eddb7e0c7e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/TropicalFish_PatternMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/TropicalFish_PatternMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fish; -import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.entity.animal.fish.TropicalFish; import org.spongepowered.api.data.type.TropicalFishShape; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/WaterAnimalMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/WaterAnimalMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/WaterAnimalMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/WaterAnimalMixin_API.java index 8ae63dc801e..bff1d7b0200 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/WaterAnimalMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fish/WaterAnimalMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fish; -import net.minecraft.world.entity.animal.WaterAnimal; +import net.minecraft.world.entity.animal.fish.WaterAnimal; import org.spongepowered.api.entity.living.aquatic.Aquatic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.mixin.api.minecraft.world.entity.MobMixin_API; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/FoxMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fox/FoxMixin_API.java similarity index 85% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/FoxMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fox/FoxMixin_API.java index 5fe142e3f62..b3bcea4ad09 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/FoxMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fox/FoxMixin_API.java @@ -22,8 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fox; +import net.minecraft.world.entity.EntityReference; +import net.minecraft.world.entity.LivingEntity; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.Fox; @@ -33,21 +35,24 @@ import org.spongepowered.asm.mixin.Intrinsic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AnimalMixin_API; import java.util.Set; import java.util.UUID; +import java.util.stream.Stream; -@Mixin(net.minecraft.world.entity.animal.Fox.class) +@Mixin(net.minecraft.world.entity.animal.fox.Fox.class) @Implements(@Interface(iface = Fox.class, prefix = "fox$", remap = Remap.NONE)) public abstract class FoxMixin_API extends AnimalMixin_API implements Fox { // @formatter:off - @Shadow protected abstract boolean shadow$trusts(UUID p_213468_1_); + @Shadow abstract Stream> shadow$getTrustedEntities(); // @formatter:on + @Intrinsic public boolean fox$trusts(UUID uniqueId) { - return this.shadow$trusts(uniqueId); + return this.shadow$getTrustedEntities().map(EntityReference::getUUID).anyMatch(uniqueId::equals); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Fox_TypeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fox/Fox_VariantMixin_API.java similarity index 90% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Fox_TypeMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fox/Fox_VariantMixin_API.java index 3e25a5d44d2..9b44a0350a4 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Fox_TypeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/fox/Fox_VariantMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fox; -import net.minecraft.world.entity.animal.Fox; +import net.minecraft.world.entity.animal.fox.Fox; import org.spongepowered.api.data.type.FoxType; import org.spongepowered.asm.mixin.Mixin; -@Mixin(Fox.Type.class) -public abstract class Fox_TypeMixin_API implements FoxType { +@Mixin(Fox.Variant.class) +public abstract class Fox_VariantMixin_API implements FoxType { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/FrogVariantMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/frog/FrogVariantMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/FrogVariantMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/frog/FrogVariantMixin_API.java index dad1a167076..91b73533871 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/FrogVariantMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/frog/FrogVariantMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.frog; -import net.minecraft.world.entity.animal.FrogVariant; +import net.minecraft.world.entity.animal.frog.FrogVariant; import org.spongepowered.api.data.type.FrogType; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/frog/TadpoleMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/frog/TadpoleMixin_API.java index aee39cf5fc7..36bbe9578c0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/frog/TadpoleMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/frog/TadpoleMixin_API.java @@ -27,7 +27,7 @@ import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.frog.Tadpole; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AbstractFishMixin_API; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.fish.AbstractFishMixin_API; import java.util.Set; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AbstractGolemMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/AbstractGolemMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AbstractGolemMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/AbstractGolemMixin_API.java index bc1a53beaa9..58bb6ad99fe 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/AbstractGolemMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/AbstractGolemMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.golem; -import net.minecraft.world.entity.animal.AbstractGolem; +import net.minecraft.world.entity.animal.golem.AbstractGolem; import org.spongepowered.api.entity.living.golem.Golem; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.mixin.api.minecraft.world.entity.PathfinderMobMixin_API; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/CopperGolemMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/CopperGolemMixin_API.java new file mode 100644 index 00000000000..e759e96fcf0 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/CopperGolemMixin_API.java @@ -0,0 +1,33 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.golem; + +import org.spongepowered.api.entity.living.golem.CopperGolem; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(net.minecraft.world.entity.animal.golem.CopperGolem.class) +public abstract class CopperGolemMixin_API extends AbstractGolemMixin_API implements CopperGolem { + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/IronGolemMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/IronGolemMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/IronGolemMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/IronGolemMixin_API.java index d6917022ae5..6c98ba3f6e2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/IronGolemMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/IronGolemMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.golem; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.IronGolem.class) +@Mixin(net.minecraft.world.entity.animal.golem.IronGolem.class) public abstract class IronGolemMixin_API extends AbstractGolemMixin_API implements IronGolem { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SnowGolemMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/SnowGolemMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SnowGolemMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/SnowGolemMixin_API.java index e8c23f3e482..6c3f2ed4d08 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SnowGolemMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/golem/SnowGolemMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.golem; import org.spongepowered.api.entity.living.golem.SnowGolem; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.SnowGolem.class) +@Mixin(net.minecraft.world.entity.animal.golem.SnowGolem.class) public abstract class SnowGolemMixin_API extends AbstractGolemMixin_API implements SnowGolem { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/FlyingAnimalMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/happyghast/HappyGhastMixin_API.java similarity index 86% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/FlyingAnimalMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/happyghast/HappyGhastMixin_API.java index 0d566309c56..d3f163cb5e7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/FlyingAnimalMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/happyghast/HappyGhastMixin_API.java @@ -22,12 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.happyghast; -import net.minecraft.world.entity.animal.FlyingAnimal; -import org.spongepowered.api.entity.Aerial; +import org.spongepowered.api.entity.living.animal.HappyGhast; import org.spongepowered.asm.mixin.Mixin; -@Mixin(FlyingAnimal.class) -public interface FlyingAnimalMixin_API extends Aerial { +@Mixin(net.minecraft.world.entity.animal.happyghast.HappyGhast.class) +public abstract class HappyGhastMixin_API implements HappyGhast { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/nautilus/AbstractNautilusMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/nautilus/AbstractNautilusMixin_API.java new file mode 100644 index 00000000000..9104f1b2163 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/nautilus/AbstractNautilusMixin_API.java @@ -0,0 +1,63 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.nautilus; + +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.animal.nautilus.AbstractNautilus; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.entity.living.animal.NautilusLike; +import org.spongepowered.api.item.inventory.Carrier; +import org.spongepowered.api.item.inventory.type.CarriedInventory; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.inventory.util.InventoryUtil; +import org.spongepowered.common.mixin.api.minecraft.world.entity.TamableAnimalMixin_API; + +import java.util.Set; + +@Mixin(AbstractNautilus.class) +public abstract class AbstractNautilusMixin_API extends TamableAnimalMixin_API implements NautilusLike { + + @Shadow protected SimpleContainer inventory; + + @Override + protected Set> api$getVanillaValues() { + final Set> values = super.api$getVanillaValues(); + + values.add(this.requireValue(Keys.MAX_HEALTH).asImmutable()); + values.add(this.requireValue(Keys.IS_TAMED).asImmutable()); + + this.getValue(Keys.OWNER).map(Value::asImmutable).ifPresent(values::add); + + return values; + } + + + @Override + public CarriedInventory inventory() { + return InventoryUtil.carriedWrapperInventory(this.inventory, this); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/nautilus/NautilusMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/nautilus/NautilusMixin_API.java new file mode 100644 index 00000000000..6d7055a6163 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/nautilus/NautilusMixin_API.java @@ -0,0 +1,32 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.nautilus; + +import org.spongepowered.api.entity.living.animal.Nautilus; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(net.minecraft.world.entity.animal.nautilus.Nautilus.class) +public abstract class NautilusMixin_API extends AbstractNautilusMixin_API implements Nautilus { +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/nautilus/ZombieNautilusMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/nautilus/ZombieNautilusMixin_API.java new file mode 100644 index 00000000000..d01987a1957 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/nautilus/ZombieNautilusMixin_API.java @@ -0,0 +1,32 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.nautilus; + +import org.spongepowered.api.entity.living.animal.ZombieNautilus; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(net.minecraft.world.entity.animal.nautilus.ZombieNautilus.class) +public abstract class ZombieNautilusMixin_API extends AbstractNautilusMixin_API implements ZombieNautilus { +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PandaMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/panda/PandaMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PandaMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/panda/PandaMixin_API.java index 1c801518f22..8ed5f345a23 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PandaMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/panda/PandaMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.panda; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.Panda; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AnimalMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Panda.class) +@Mixin(net.minecraft.world.entity.animal.panda.Panda.class) public abstract class PandaMixin_API extends AnimalMixin_API implements Panda { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Panda_GeneMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/panda/Panda_GeneMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Panda_GeneMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/panda/Panda_GeneMixin_API.java index b4648c09241..b200fa35273 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Panda_GeneMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/panda/Panda_GeneMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.panda; -import net.minecraft.world.entity.animal.Panda; +import net.minecraft.world.entity.animal.panda.Panda; import org.spongepowered.api.data.type.PandaGene; import org.spongepowered.asm.mixin.Implements; import org.spongepowered.asm.mixin.Interface; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/ParrotMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/parrot/ParrotMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/ParrotMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/parrot/ParrotMixin_API.java index 4b11b38bb91..71d2039381f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/ParrotMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/parrot/ParrotMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.parrot; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Parrot.class) +@Mixin(net.minecraft.world.entity.animal.parrot.Parrot.class) public abstract class ParrotMixin_API extends ShoulderRidingEntityMixin_API implements Parrot { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Parrot_VariantMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/parrot/Parrot_VariantMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Parrot_VariantMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/parrot/Parrot_VariantMixin_API.java index b9b6f5798a9..4e83cb7e5a3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Parrot_VariantMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/parrot/Parrot_VariantMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.parrot; import org.spongepowered.api.data.type.ParrotType; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.Parrot.Variant.class) +@Mixin(net.minecraft.world.entity.animal.parrot.Parrot.Variant.class) public abstract class Parrot_VariantMixin_API implements ParrotType { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/ShoulderRidingEntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/parrot/ShoulderRidingEntityMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/ShoulderRidingEntityMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/parrot/ShoulderRidingEntityMixin_API.java index 44e88b5417b..50a696ee04d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/ShoulderRidingEntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/parrot/ShoulderRidingEntityMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.parrot; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.mixin.api.minecraft.world.entity.TamableAnimalMixin_API; -@Mixin(net.minecraft.world.entity.animal.ShoulderRidingEntity.class) +@Mixin(net.minecraft.world.entity.animal.parrot.ShoulderRidingEntity.class) public abstract class ShoulderRidingEntityMixin_API extends TamableAnimalMixin_API { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PigMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/pig/PigMixin_API.java similarity index 92% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PigMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/pig/PigMixin_API.java index 99a9bb2709b..a77790bf21d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PigMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/pig/PigMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.pig; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.Pig; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AnimalMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Pig.class) +@Mixin(net.minecraft.world.entity.animal.pig.Pig.class) public abstract class PigMixin_API extends AnimalMixin_API implements Pig { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/FlyingMobMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/pig/PigVariantMixin_API.java similarity index 87% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/FlyingMobMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/pig/PigVariantMixin_API.java index eb413d88e1f..b374b70822d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/FlyingMobMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/pig/PigVariantMixin_API.java @@ -22,13 +22,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.pig; -import net.minecraft.world.entity.FlyingMob; -import org.spongepowered.api.entity.Aerial; +import net.minecraft.world.entity.animal.pig.PigVariant; +import org.spongepowered.api.data.type.PigType; import org.spongepowered.asm.mixin.Mixin; -@Mixin(FlyingMob.class) -public abstract class FlyingMobMixin_API extends MobMixin_API implements Aerial { +@Mixin(PigVariant.class) +public class PigVariantMixin_API implements PigType { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PolarBearMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/polarbear/PolarBearMixin_API.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PolarBearMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/polarbear/PolarBearMixin_API.java index ffc308a242b..60bdbd05d19 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/PolarBearMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/polarbear/PolarBearMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.polarbear; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.PolarBear; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AnimalMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.PolarBear.class) +@Mixin(net.minecraft.world.entity.animal.polarbear.PolarBear.class) public abstract class PolarBearMixin_API extends AnimalMixin_API implements PolarBear { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/RabbitMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/rabbit/RabbitMixin_API.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/RabbitMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/rabbit/RabbitMixin_API.java index a4a60b1fef3..23262a67025 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/RabbitMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/rabbit/RabbitMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.rabbit; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.Rabbit; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AnimalMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Rabbit.class) +@Mixin(net.minecraft.world.entity.animal.rabbit.Rabbit.class) public abstract class RabbitMixin_API extends AnimalMixin_API implements Rabbit { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Rabbit_VariantMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/rabbit/Rabbit_VariantMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Rabbit_VariantMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/rabbit/Rabbit_VariantMixin_API.java index 424366ea007..94a1fb9e31e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/Rabbit_VariantMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/rabbit/Rabbit_VariantMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.rabbit; import org.spongepowered.api.data.type.RabbitType; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.animal.Rabbit.Variant.class) +@Mixin(net.minecraft.world.entity.animal.rabbit.Rabbit.Variant.class) public abstract class Rabbit_VariantMixin_API implements RabbitType { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SheepMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/sheep/SheepMixin_API.java similarity index 92% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SheepMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/sheep/SheepMixin_API.java index 6e08bb1f5f1..ffd86fe315f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SheepMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/sheep/SheepMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.sheep; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.Sheep; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AnimalMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Sheep.class) +@Mixin(net.minecraft.world.entity.animal.sheep.Sheep.class) public abstract class SheepMixin_API extends AnimalMixin_API implements Sheep { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/GlowSquidMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/squid/GlowSquidMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/GlowSquidMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/squid/GlowSquidMixin_API.java index de783fb269b..08daa57f927 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/GlowSquidMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/squid/GlowSquidMixin_API.java @@ -22,11 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.squid; import org.spongepowered.api.entity.living.aquatic.GlowSquid; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.GlowSquid.class) +@Mixin(net.minecraft.world.entity.animal.squid.GlowSquid.class) public abstract class GlowSquidMixin_API implements GlowSquid { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SquidMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/squid/SquidMixin_API.java similarity index 83% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SquidMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/squid/SquidMixin_API.java index 09582809680..7f20da49d1e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/SquidMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/squid/SquidMixin_API.java @@ -22,12 +22,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.squid; import org.spongepowered.api.entity.living.aquatic.Squid; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AgeableWaterCreatureMixin_API; -@Mixin(net.minecraft.world.entity.animal.Squid.class) -public abstract class SquidMixin_API extends WaterAnimalMixin_API implements Squid { +@Mixin(net.minecraft.world.entity.animal.squid.Squid.class) +public abstract class SquidMixin_API extends AgeableWaterCreatureMixin_API implements Squid { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/TurtleMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/turtle/TurtleMixin_API.java similarity index 92% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/TurtleMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/turtle/TurtleMixin_API.java index af8c0ac2d78..b1790c8a505 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/TurtleMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/turtle/TurtleMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.turtle; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.animal.Turtle; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AnimalMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Turtle.class) +@Mixin(net.minecraft.world.entity.animal.turtle.Turtle.class) public abstract class TurtleMixin_API extends AnimalMixin_API implements Turtle { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/WolfMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/wolf/WolfMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/WolfMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/wolf/WolfMixin_API.java index 79fbc1d7f32..8ca6fbe1b2c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/WolfMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/wolf/WolfMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.wolf; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -32,7 +32,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.animal.Wolf.class) +@Mixin(net.minecraft.world.entity.animal.wolf.Wolf.class) public abstract class WolfMixin_API extends TamableAnimalMixin_API implements Wolf { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/PaintingVariantMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/wolf/WolfSoundVariantMixin_API.java similarity index 86% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/PaintingVariantMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/wolf/WolfSoundVariantMixin_API.java index e8d4d2f761b..ad15d496e5a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/PaintingVariantMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/wolf/WolfSoundVariantMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.decoration; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.wolf; -import org.spongepowered.api.data.type.ArtType; +import net.minecraft.world.entity.animal.wolf.WolfSoundVariant; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.decoration.PaintingVariant.class) -public abstract class PaintingVariantMixin_API implements ArtType { +@Mixin(WolfSoundVariant.class) +public class WolfSoundVariantMixin_API implements org.spongepowered.api.data.type.WolfSoundVariant { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/WollfVariantMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/wolf/WolfVariantMixin_API.java similarity index 89% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/WollfVariantMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/wolf/WolfVariantMixin_API.java index 9a9e8013290..7f9b98a84f6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/WollfVariantMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/animal/wolf/WolfVariantMixin_API.java @@ -22,11 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.animal; +package org.spongepowered.common.mixin.api.minecraft.world.entity.animal.wolf; -import net.minecraft.world.entity.animal.WolfVariant; +import net.minecraft.world.entity.animal.wolf.WolfVariant; import org.spongepowered.asm.mixin.Mixin; @Mixin(WolfVariant.class) -public class WollfVariantMixin_API implements org.spongepowered.api.data.type.WolfVariant { +public class WolfVariantMixin_API implements org.spongepowered.api.data.type.WolfVariant { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/enderdragon/EndCrystalMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/enderdragon/EndCrystalMixin_API.java index 7f4490eef6a..0d9c4860db7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/enderdragon/EndCrystalMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/enderdragon/EndCrystalMixin_API.java @@ -39,8 +39,7 @@ public abstract class EndCrystalMixin_API extends EntityMixin_API implements End @Override public void detonate() { this.shadow$discard(); - ((EndCrystalBridge) this).bridge$throwExplosionEventAndExplode(this.shadow$getCommandSenderWorld(), null, this.shadow$getX(), - this.shadow$getY(), this.shadow$getZ(), true, null); + ((EndCrystalBridge) this).bridge$wrappedExplode(this.shadow$getX(), this.shadow$getY(), this.shadow$getZ(), null, null); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/enderdragon/EnderDragonMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/enderdragon/EnderDragonMixin_API.java index 1d5ea3ce658..593c5647d80 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/enderdragon/EnderDragonMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/enderdragon/EnderDragonMixin_API.java @@ -43,7 +43,7 @@ public abstract class EnderDragonMixin_API extends MobMixin_API implements EnderDragon { // @formatter:off - @Shadow @Final private net.minecraft.world.entity.boss.EnderDragonPart[] subEntities; + @Shadow @Final private net.minecraft.world.entity.boss.enderdragon.EnderDragonPart[] subEntities; @Shadow @Final private EnderDragonPhaseManager phaseManager; // @formatter:on @@ -51,7 +51,7 @@ public abstract class EnderDragonMixin_API extends MobMixin_API implements Ender public Set parts() { Builder builder = ImmutableSet.builder(); - for (net.minecraft.world.entity.boss.EnderDragonPart part : this.subEntities) { + for (net.minecraft.world.entity.boss.enderdragon.EnderDragonPart part : this.subEntities) { builder.add((EnderDragonPart) part); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/EnderDragonPartMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/enderdragon/EnderDragonPartMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/EnderDragonPartMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/enderdragon/EnderDragonPartMixin_API.java index 2018ad2420b..75068e50e3c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/EnderDragonPartMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/boss/enderdragon/EnderDragonPartMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.boss; +package org.spongepowered.common.mixin.api.minecraft.world.entity.boss.enderdragon; import org.spongepowered.api.entity.living.monster.boss.dragon.EnderDragon; import org.spongepowered.api.entity.living.monster.boss.dragon.EnderDragonPart; @@ -31,7 +31,7 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.mixin.api.minecraft.world.entity.EntityMixin_API; -@Mixin(net.minecraft.world.entity.boss.EnderDragonPart.class) +@Mixin(net.minecraft.world.entity.boss.enderdragon.EnderDragonPart.class) public abstract class EnderDragonPartMixin_API extends EntityMixin_API implements EnderDragonPart { // @formatter:off diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/PaintingMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/painting/PaintingMixin_API.java similarity index 90% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/PaintingMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/painting/PaintingMixin_API.java index 6182867cd8c..d3da6aea61a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/PaintingMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/painting/PaintingMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.decoration; +package org.spongepowered.common.mixin.api.minecraft.world.entity.decoration.painting; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.hanging.Painting; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.decoration.HangingEntityMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.decoration.Painting.class) +@Mixin(net.minecraft.world.entity.decoration.painting.Painting.class) public abstract class PaintingMixin_API extends HangingEntityMixin_API implements Painting { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/painting/PaintingVariantMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/painting/PaintingVariantMixin_API.java new file mode 100644 index 00000000000..21d95cde7a4 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/decoration/painting/PaintingVariantMixin_API.java @@ -0,0 +1,42 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.decoration.painting; + +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.data.type.ArtType; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.util.DataPackUtil; + +import java.util.Optional; + +@Mixin(net.minecraft.world.entity.decoration.painting.PaintingVariant.class) +public abstract class PaintingVariantMixin_API implements ArtType { + + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, net.minecraft.world.entity.decoration.painting.PaintingVariant.DIRECT_CODEC, (net.minecraft.world.entity.decoration.painting.PaintingVariant) (Object) this); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/GhastMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/GhastMixin_API.java index 1889b3331da..c05d9c3ee35 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/GhastMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/GhastMixin_API.java @@ -26,9 +26,9 @@ import org.spongepowered.api.entity.living.monster.Ghast; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.common.mixin.api.minecraft.world.entity.FlyingMobMixin_API; +import org.spongepowered.common.mixin.api.minecraft.world.entity.MobMixin_API; @Mixin(net.minecraft.world.entity.monster.Ghast.class) -public abstract class GhastMixin_API extends FlyingMobMixin_API implements Ghast { +public abstract class GhastMixin_API extends MobMixin_API implements Ghast { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PhantomMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PhantomMixin_API.java index 0c9d98220b7..718e9a5327a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PhantomMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PhantomMixin_API.java @@ -28,12 +28,12 @@ import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.monster.Phantom; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.common.mixin.api.minecraft.world.entity.FlyingMobMixin_API; +import org.spongepowered.common.mixin.api.minecraft.world.entity.MobMixin_API; import java.util.Set; @Mixin(net.minecraft.world.entity.monster.Phantom.class) -public abstract class PhantomMixin_API extends FlyingMobMixin_API implements Phantom { +public abstract class PhantomMixin_API extends MobMixin_API implements Phantom { @Override protected Set> api$getVanillaValues() { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ShulkerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ShulkerMixin_API.java index 3a0ed7b26d7..28a25f70e15 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ShulkerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ShulkerMixin_API.java @@ -32,7 +32,7 @@ import org.spongepowered.api.entity.projectile.Projectile; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.entity.projectile.ProjectileUtil; -import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.AbstractGolemMixin_API; +import org.spongepowered.common.mixin.api.minecraft.world.entity.animal.golem.AbstractGolemMixin_API; import java.util.Objects; import java.util.Optional; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/BreezeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/breeze/BreezeMixin_API.java similarity index 93% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/BreezeMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/breeze/BreezeMixin_API.java index dc84063fb15..0edbfbb5754 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/BreezeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/breeze/BreezeMixin_API.java @@ -22,10 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.breeze; import org.spongepowered.api.entity.living.monster.breeze.Breeze; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.monster.MonsterMixin_API; @Mixin(net.minecraft.world.entity.monster.breeze.Breeze.class) public abstract class BreezeMixin_API extends MonsterMixin_API implements Breeze { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SlimeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/cubemob/AbstractCubeMobMixin_API.java similarity index 89% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SlimeMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/cubemob/AbstractCubeMobMixin_API.java index d856a84805a..f23b15ced43 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SlimeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/cubemob/AbstractCubeMobMixin_API.java @@ -22,18 +22,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.cubemob; +import net.minecraft.world.entity.monster.cubemob.AbstractCubeMob; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; -import org.spongepowered.api.entity.living.slime.Slime; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.common.mixin.api.minecraft.world.entity.MobMixin_API; +import org.spongepowered.common.mixin.api.minecraft.world.entity.AgeableMobMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.monster.Slime.class) -public abstract class SlimeMixin_API extends MobMixin_API implements Slime { +@Mixin(AbstractCubeMob.class) +public abstract class AbstractCubeMobMixin_API extends AgeableMobMixin_API { @Override protected Set> api$getVanillaValues() { @@ -44,5 +44,4 @@ public abstract class SlimeMixin_API extends MobMixin_API implements Slime { return values; } - } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/MagmaCubeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/cubemob/MagmaCubeMixin_API.java similarity index 88% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/MagmaCubeMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/cubemob/MagmaCubeMixin_API.java index 201e358d58b..bdb88e2f1ba 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/MagmaCubeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/cubemob/MagmaCubeMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.cubemob; import org.spongepowered.api.entity.living.slime.MagmaCube; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.monster.MagmaCube.class) -public abstract class MagmaCubeMixin_API extends SlimeMixin_API implements MagmaCube { +@Mixin(net.minecraft.world.entity.monster.cubemob.MagmaCube.class) +public abstract class MagmaCubeMixin_API extends AbstractCubeMobMixin_API implements MagmaCube { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/cubemob/SlimeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/cubemob/SlimeMixin_API.java new file mode 100644 index 00000000000..d9095d7f370 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/cubemob/SlimeMixin_API.java @@ -0,0 +1,33 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.cubemob; + +import org.spongepowered.api.entity.living.slime.Slime; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(net.minecraft.world.entity.monster.cubemob.Slime.class) +public abstract class SlimeMixin_API extends AbstractCubeMobMixin_API implements Slime { + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/HoglinMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/hoglin/HoglinMixin_API.java similarity index 98% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/HoglinMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/hoglin/HoglinMixin_API.java index 566a80e54ca..d1af4d6b6de 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/HoglinMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/hoglin/HoglinMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.hoglin; import org.spongepowered.api.entity.living.animal.Hoglin; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/AbstractIllagerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/AbstractIllagerMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/AbstractIllagerMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/AbstractIllagerMixin_API.java index 8843ee358c6..c9ce5d9216f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/AbstractIllagerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/AbstractIllagerMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.illager; -import net.minecraft.world.entity.monster.AbstractIllager; +import net.minecraft.world.entity.monster.illager.AbstractIllager; import org.spongepowered.api.entity.living.monster.raider.illager.Illager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.mixin.api.minecraft.world.entity.raid.RaiderMixin_API; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/EvokerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/EvokerMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/EvokerMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/EvokerMixin_API.java index bfa2e906e67..08f90069f81 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/EvokerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/EvokerMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.illager; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.monster.Evoker.class) +@Mixin(net.minecraft.world.entity.monster.illager.Evoker.class) public abstract class EvokerMixin_API extends SpellcasterIllagerMixin_API implements Evoker { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/IllusionerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/IllusionerMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/IllusionerMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/IllusionerMixin_API.java index d3543f85819..b24b6f7f2a2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/IllusionerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/IllusionerMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.illager; import org.spongepowered.api.entity.living.monster.raider.illager.spellcaster.Illusioner; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.monster.Illusioner.class) +@Mixin(net.minecraft.world.entity.monster.illager.Illusioner.class) public abstract class IllusionerMixin_API extends SpellcasterIllagerMixin_API implements Illusioner { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PillagerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/PillagerMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PillagerMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/PillagerMixin_API.java index 64e7ef99122..0fc56689a09 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PillagerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/PillagerMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.illager; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.monster.Pillager.class) +@Mixin(net.minecraft.world.entity.monster.illager.Pillager.class) public abstract class PillagerMixin_API extends AbstractIllagerMixin_API implements Pillager { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SpellcasterIllagerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/SpellcasterIllagerMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SpellcasterIllagerMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/SpellcasterIllagerMixin_API.java index 76a27df8baf..e421c14e7be 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SpellcasterIllagerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/SpellcasterIllagerMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.illager; -import net.minecraft.world.entity.monster.SpellcasterIllager; +import net.minecraft.world.entity.monster.illager.SpellcasterIllager; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.monster.raider.illager.spellcaster.Spellcaster; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SpellcasterIllager_IllagerSpellMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/SpellcasterIllager_IllagerSpellMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SpellcasterIllager_IllagerSpellMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/SpellcasterIllager_IllagerSpellMixin_API.java index e7b900dbfc9..74477199c0e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SpellcasterIllager_IllagerSpellMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/SpellcasterIllager_IllagerSpellMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.illager; -import net.minecraft.world.entity.monster.SpellcasterIllager; +import net.minecraft.world.entity.monster.illager.SpellcasterIllager; import org.spongepowered.api.data.type.SpellType; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/VindicatorMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/VindicatorMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/VindicatorMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/VindicatorMixin_API.java index 2053431df08..f5f5cdff131 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/VindicatorMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/illager/VindicatorMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.illager; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.monster.Vindicator.class) +@Mixin(net.minecraft.world.entity.monster.illager.Vindicator.class) public abstract class VindicatorMixin_API extends AbstractIllagerMixin_API implements Vindicator { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/AbstractPiglinMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/piglin/AbstractPiglinMixin_API.java similarity index 98% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/AbstractPiglinMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/piglin/AbstractPiglinMixin_API.java index a06f5e83730..d4d7686e79b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/AbstractPiglinMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/piglin/AbstractPiglinMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.piglin; import net.minecraft.world.entity.monster.piglin.AbstractPiglin; import org.spongepowered.api.entity.living.monster.piglin.PiglinLike; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PiglinBruteMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/piglin/PiglinBruteMixin_API.java similarity index 98% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PiglinBruteMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/piglin/PiglinBruteMixin_API.java index ff57494d1e7..90869eb2712 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PiglinBruteMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/piglin/PiglinBruteMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.piglin; import org.spongepowered.api.entity.living.monster.piglin.PiglinBrute; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PiglinMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/piglin/PiglinMixin_API.java similarity index 98% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PiglinMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/piglin/PiglinMixin_API.java index 07d859aa8cf..6447ac82a16 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/PiglinMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/piglin/PiglinMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.piglin; import org.spongepowered.api.entity.living.monster.piglin.Piglin; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/AbstractSkeletonMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/AbstractSkeletonMixin_API.java similarity index 89% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/AbstractSkeletonMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/AbstractSkeletonMixin_API.java index da02f7d8a40..dd8833642cd 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/AbstractSkeletonMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/AbstractSkeletonMixin_API.java @@ -22,11 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.skeleton; -import net.minecraft.world.entity.monster.AbstractSkeleton; +import net.minecraft.world.entity.monster.skeleton.AbstractSkeleton; import org.spongepowered.api.entity.living.monster.skeleton.SkeletonLike; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.monster.MonsterMixin_API; @Mixin(AbstractSkeleton.class) public abstract class AbstractSkeletonMixin_API extends MonsterMixin_API implements SkeletonLike { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/BoggedMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/BoggedMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/BoggedMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/BoggedMixin_API.java index 6dc064db04e..2cb15655d58 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/BoggedMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/BoggedMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.skeleton; import org.spongepowered.api.entity.living.monster.skeleton.Bogged; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.monster.Bogged.class) +@Mixin(net.minecraft.world.entity.monster.skeleton.Bogged.class) public abstract class BoggedMixin_API extends AbstractSkeletonMixin_API implements Bogged { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/ParchedMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/ParchedMixin_API.java new file mode 100644 index 00000000000..f201b9abd60 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/ParchedMixin_API.java @@ -0,0 +1,33 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.skeleton; + +import org.spongepowered.api.entity.living.monster.skeleton.Parched; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(net.minecraft.world.entity.monster.skeleton.Parched.class) +public abstract class ParchedMixin_API extends AbstractSkeletonMixin_API implements Parched { + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SkeletonMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/SkeletonMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SkeletonMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/SkeletonMixin_API.java index 5256da67b7e..10a4b5cff05 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SkeletonMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/SkeletonMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.skeleton; import org.spongepowered.api.entity.living.monster.skeleton.Skeleton; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.monster.Skeleton.class) +@Mixin(net.minecraft.world.entity.monster.skeleton.Skeleton.class) public abstract class SkeletonMixin_API extends AbstractSkeletonMixin_API implements Skeleton { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/StrayMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/StrayMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/StrayMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/StrayMixin_API.java index 598393f3d6e..a9753f77498 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/StrayMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/StrayMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.skeleton; import org.spongepowered.api.entity.living.monster.skeleton.Stray; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.monster.Stray.class) +@Mixin(net.minecraft.world.entity.monster.skeleton.Stray.class) public abstract class StrayMixin_API extends AbstractSkeletonMixin_API implements Stray { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/WitherSkeletonMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/WitherSkeletonMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/WitherSkeletonMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/WitherSkeletonMixin_API.java index 0b2370c82f4..d094112c222 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/WitherSkeletonMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/skeleton/WitherSkeletonMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.skeleton; import org.spongepowered.api.entity.living.monster.skeleton.WitherSkeleton; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.monster.WitherSkeleton.class) +@Mixin(net.minecraft.world.entity.monster.skeleton.WitherSkeleton.class) public abstract class WitherSkeletonMixin_API extends AbstractSkeletonMixin_API implements WitherSkeleton { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/CaveSpiderMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/spider/CaveSpiderMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/CaveSpiderMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/spider/CaveSpiderMixin_API.java index d4642c10802..f6d75889526 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/CaveSpiderMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/spider/CaveSpiderMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.spider; import org.spongepowered.api.entity.living.monster.spider.CaveSpider; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.monster.CaveSpider.class) +@Mixin(net.minecraft.world.entity.monster.spider.CaveSpider.class) public abstract class CaveSpiderMixin_API extends SpiderMixin_API implements CaveSpider { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SpiderMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/spider/SpiderMixin_API.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SpiderMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/spider/SpiderMixin_API.java index dee3c008865..c2c2009a9d1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/SpiderMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/spider/SpiderMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.spider; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.monster.spider.Spider; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.monster.MonsterMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.monster.Spider.class) +@Mixin(net.minecraft.world.entity.monster.spider.Spider.class) public abstract class SpiderMixin_API extends MonsterMixin_API implements Spider { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/DrownedMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/DrownedMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/DrownedMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/DrownedMixin_API.java index 1df42a64f8a..b016c59527a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/DrownedMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/DrownedMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.zombie; import org.spongepowered.api.entity.living.monster.zombie.Drowned; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.monster.Drowned.class) +@Mixin(net.minecraft.world.entity.monster.zombie.Drowned.class) public abstract class DrownedMixin_API extends ZombieMixin_API implements Drowned { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/HuskMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/HuskMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/HuskMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/HuskMixin_API.java index 977efde3f47..fe5303be2c4 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/HuskMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/HuskMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.zombie; import org.spongepowered.api.entity.living.monster.zombie.Husk; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.monster.Husk.class) +@Mixin(net.minecraft.world.entity.monster.zombie.Husk.class) public abstract class HuskMixin_API extends ZombieMixin_API implements Husk { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ZombieMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/ZombieMixin_API.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ZombieMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/ZombieMixin_API.java index f2bda1f4acb..bf5b20f310b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ZombieMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/ZombieMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.zombie; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.monster.zombie.Zombie; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.monster.MonsterMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.monster.Zombie.class) +@Mixin(net.minecraft.world.entity.monster.zombie.Zombie.class) public abstract class ZombieMixin_API extends MonsterMixin_API implements Zombie { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ZombieVillagerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/ZombieVillagerMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ZombieVillagerMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/ZombieVillagerMixin_API.java index c649140cf59..41061c9d42b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ZombieVillagerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/ZombieVillagerMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.zombie; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.monster.ZombieVillager.class) +@Mixin(net.minecraft.world.entity.monster.zombie.ZombieVillager.class) public abstract class ZombieVillagerMixin_API extends ZombieMixin_API implements ZombieVillager { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ZombifiedPiglinMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/ZombifiedPiglinMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ZombifiedPiglinMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/ZombifiedPiglinMixin_API.java index a86f41dfb40..09d78f74d03 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/ZombifiedPiglinMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/monster/zombie/ZombifiedPiglinMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.monster; +package org.spongepowered.common.mixin.api.minecraft.world.entity.monster.zombie; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.monster.ZombifiedPiglin.class) +@Mixin(net.minecraft.world.entity.monster.zombie.ZombifiedPiglin.class) public abstract class ZombifiedPiglinMixin_API extends ZombieMixin_API implements ZombifiedPiglin { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/AbstractVillagerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/AbstractVillagerMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/AbstractVillagerMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/AbstractVillagerMixin_API.java index e60b8d0166d..d0ea92835ad 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/AbstractVillagerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/AbstractVillagerMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.npc; +package org.spongepowered.common.mixin.api.minecraft.world.entity.npc.villager; -import net.minecraft.world.entity.npc.AbstractVillager; +import net.minecraft.world.entity.npc.villager.AbstractVillager; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.trader.VillagerLike; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/VillagerMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/VillagerMixin_API.java index 1b3603129df..c2fdcddd70c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/VillagerMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.npc; +package org.spongepowered.common.mixin.api.minecraft.world.entity.npc.villager; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.npc.Villager.class) +@Mixin(net.minecraft.world.entity.npc.villager.Villager.class) public abstract class VillagerMixin_API extends AbstractVillagerMixin_API implements Villager { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerProfessionMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/VillagerProfessionMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerProfessionMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/VillagerProfessionMixin_API.java index 2f6d3d6700e..bcf1e8769d5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerProfessionMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/VillagerProfessionMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.npc; +package org.spongepowered.common.mixin.api.minecraft.world.entity.npc.villager; -import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.villager.VillagerProfession; import org.spongepowered.api.data.type.ProfessionType; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerTypeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/VillagerTypeMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerTypeMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/VillagerTypeMixin_API.java index bbe9082a9a4..0c87bd0aa47 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/VillagerTypeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/villager/VillagerTypeMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.npc; +package org.spongepowered.common.mixin.api.minecraft.world.entity.npc.villager; -import net.minecraft.world.entity.npc.VillagerType; +import net.minecraft.world.entity.npc.villager.VillagerType; import org.spongepowered.asm.mixin.Mixin; @Mixin(VillagerType.class) diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/WanderingTraderMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/wanderingtrader/WanderingTraderMixin_API.java similarity index 90% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/WanderingTraderMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/wanderingtrader/WanderingTraderMixin_API.java index f9e31164eae..11a4667f181 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/WanderingTraderMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/npc/wanderingtrader/WanderingTraderMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.npc; +package org.spongepowered.common.mixin.api.minecraft.world.entity.npc.wanderingtrader; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.living.trader.WanderingTrader; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.npc.villager.AbstractVillagerMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.npc.WanderingTrader.class) +@Mixin(net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader.class) public abstract class WanderingTraderMixin_API extends AbstractVillagerMixin_API implements WanderingTrader { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/player/ChatVisiblityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/player/ChatVisiblityMixin_API.java index 22d8a0c06de..3e54cb6d300 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/player/ChatVisiblityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/player/ChatVisiblityMixin_API.java @@ -29,17 +29,18 @@ import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.adventure.SpongeAdventure; @Mixin(ChatVisiblity.class) public abstract class ChatVisiblityMixin_API implements org.spongepowered.api.entity.living.player.chat.ChatVisibility { // @formatter:off - @Shadow @Final private String key; + @Shadow @Final private net.minecraft.network.chat.Component caption; // @formatter:on @Override public Component asComponent() { - return Component.translatable(this.key); + return SpongeAdventure.asAdventure(this.caption); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/player/PlayerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/player/PlayerMixin_API.java index 28b4c304ef8..a4fc6372810 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/player/PlayerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/player/PlayerMixin_API.java @@ -38,6 +38,7 @@ import org.spongepowered.api.effect.sound.music.MusicDisc; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.item.inventory.ItemStackLike; import org.spongepowered.asm.mixin.Implements; import org.spongepowered.asm.mixin.Interface; import org.spongepowered.asm.mixin.Interface.Remap; @@ -111,6 +112,11 @@ public String name() { // Viewer + @Override + public void playTotemOfUndyingEffect(final ItemStackLike stack) { + ((ViewerBridge) this).bridge$sendToViewer(ViewerPacketUtil.totemOfUndying(this, stack)); + } + @Override public void playMusicDisc(final int x, final int y, final int z, final MusicDisc musicDisc) { ((ViewerBridge) this).bridge$sendToViewer(ViewerPacketUtil.playMusicDisc(x, y, z, musicDisc, this.shadow$level().registryAccess())); @@ -135,7 +141,7 @@ public void resetBlockProgress(final int x, final int y, final int z) { @Override public void playSound(final Sound sound, final double x, final double y, final double z) { - ((ViewerBridge) this).bridge$sendToViewer(ViewerPacketUtil.playSound(sound, this.shadow$level().random, x, y, z)); + ((ViewerBridge) this).bridge$sendToViewer(ViewerPacketUtil.playSound(sound, this.shadow$level().getRandom(), x, y, z)); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/FireworkRocketEntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/FireworkRocketEntityMixin_API.java index 304d79b2767..316e2ef64f9 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/FireworkRocketEntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/FireworkRocketEntityMixin_API.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.projectile.FireworkRocketEntity; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -37,12 +38,14 @@ public abstract class FireworkRocketEntityMixin_API extends ProjectileMixin_API implements FireworkRocket { // @formatter:off - @Shadow protected abstract void shadow$explode(); + @Shadow protected abstract void shadow$explode(ServerLevel level); // @formatter:on @Override public void detonate() { - this.shadow$explode(); + if (this.shadow$level() instanceof ServerLevel sl) { + this.shadow$explode(sl); + } } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/AbstractArrowMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/AbstractArrowMixin_API.java similarity index 92% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/AbstractArrowMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/AbstractArrowMixin_API.java index 14702b4832c..677e9cd3641 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/AbstractArrowMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/AbstractArrowMixin_API.java @@ -22,13 +22,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.arrow; -import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.entity.projectile.arrow.AbstractArrow; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.projectile.arrow.ArrowLike; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.ProjectileMixin_API; import java.util.Set; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/AbstractArrow_PickupMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/AbstractArrow_PickupMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/AbstractArrow_PickupMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/AbstractArrow_PickupMixin_API.java index 2b6317f72bc..af2ee6cfd1f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/AbstractArrow_PickupMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/AbstractArrow_PickupMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.arrow; -import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.entity.projectile.arrow.AbstractArrow; import org.spongepowered.api.data.type.PickupRule; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ArrowMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/ArrowMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ArrowMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/ArrowMixin_API.java index af7f6e134e5..aacaed6b0ec 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ArrowMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/ArrowMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.arrow; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.projectile.Arrow.class) +@Mixin(net.minecraft.world.entity.projectile.arrow.Arrow.class) public abstract class ArrowMixin_API extends AbstractArrowMixin_API implements Arrow { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/SpectralArrowMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/SpectralArrowMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/SpectralArrowMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/SpectralArrowMixin_API.java index f7b1de3429b..54817227838 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/SpectralArrowMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/SpectralArrowMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.arrow; import org.spongepowered.api.entity.projectile.arrow.SpectralArrow; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.projectile.SpectralArrow.class) +@Mixin(net.minecraft.world.entity.projectile.arrow.SpectralArrow.class) public abstract class SpectralArrowMixin_API extends AbstractArrowMixin_API implements SpectralArrow { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownTridentMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/ThrownTridentMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownTridentMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/ThrownTridentMixin_API.java index 4dcadc05ee9..41509d0537f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownTridentMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/arrow/ThrownTridentMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.arrow; -import net.minecraft.world.entity.projectile.ThrownTrident; +import net.minecraft.world.entity.projectile.arrow.ThrownTrident; import org.spongepowered.api.entity.projectile.arrow.Trident; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/AbstractHurtingProjectileMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectileMixin_API.java similarity index 89% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/AbstractHurtingProjectileMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectileMixin_API.java index e709112d51e..4b96313a9e8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/AbstractHurtingProjectileMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectileMixin_API.java @@ -22,13 +22,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.hurtingprojectile; -import net.minecraft.world.entity.projectile.AbstractHurtingProjectile; +import net.minecraft.world.entity.projectile.hurtingprojectile.AbstractHurtingProjectile; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.projectile.AcceleratingProjectile; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.ProjectileMixin_API; import java.util.Set; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/DragonFireballMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/DragonFireballMixin_API.java similarity index 93% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/DragonFireballMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/DragonFireballMixin_API.java index 49758fd8b01..a38feff5816 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/DragonFireballMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/DragonFireballMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.hurtingprojectile; import org.spongepowered.api.entity.projectile.explosive.fireball.DragonFireball; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.projectile.DragonFireball.class) +@Mixin(net.minecraft.world.entity.projectile.hurtingprojectile.DragonFireball.class) public abstract class DragonFireballMixin_API extends AbstractHurtingProjectileMixin_API implements DragonFireball { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/FireballMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/FireballMixin_API.java similarity index 93% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/FireballMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/FireballMixin_API.java index 3ee190cad57..1c958da47f4 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/FireballMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/FireballMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.hurtingprojectile; -import net.minecraft.world.entity.projectile.Fireball; +import net.minecraft.world.entity.projectile.hurtingprojectile.Fireball; import org.spongepowered.api.entity.projectile.IgnitingProjectile; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/LargeFireballMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/LargeFireballMixin_API.java similarity index 78% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/LargeFireballMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/LargeFireballMixin_API.java index 1956c863015..f8a822382b0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/LargeFireballMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/LargeFireballMixin_API.java @@ -22,11 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.hurtingprojectile; -import net.minecraft.world.entity.projectile.LargeFireball; -import net.minecraft.world.level.GameRules; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.projectile.hurtingprojectile.LargeFireball; import net.minecraft.world.level.Level; +import net.minecraft.world.level.gamerules.GameRules; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.projectile.explosive.fireball.ExplosiveFireball; @@ -39,14 +40,13 @@ @Mixin(LargeFireball.class) public abstract class LargeFireballMixin_API extends FireballMixin_API implements ExplosiveFireball { - @Shadow public int explosionPower; + @Shadow private int explosionPower; @Override public void detonate() { - final boolean flag = this.shadow$getCommandSenderWorld().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); - final Level.ExplosionInteraction mode = flag ? Level.ExplosionInteraction.MOB : Level.ExplosionInteraction.NONE; - ((LargeFireballBridge) this).bridge$throwExplosionEventAndExplode(this.shadow$getCommandSenderWorld(), null, this.shadow$getX(), - this.shadow$getY(), this.shadow$getZ(), this.explosionPower, flag, mode); + if (this.shadow$level() instanceof ServerLevel sl && sl.getGameRules().get(GameRules.MOB_GRIEFING)) { + ((LargeFireballBridge) this).bridge$wrappedExplode(this.shadow$getX(), this.shadow$getY(), this.shadow$getZ(), this.explosionPower, true, Level.ExplosionInteraction.MOB); + } this.shadow$discard(); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/SmallFireballMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/SmallFireballMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/SmallFireballMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/SmallFireballMixin_API.java index c7b55172d1d..8bffdf58b1b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/SmallFireballMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/SmallFireballMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.hurtingprojectile; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -31,7 +31,7 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.projectile.SmallFireball.class) +@Mixin(net.minecraft.world.entity.projectile.hurtingprojectile.SmallFireball.class) public abstract class SmallFireballMixin_API extends FireballMixin_API implements SmallFireball { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/WitherSkullMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/WitherSkullMixin_API.java similarity index 78% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/WitherSkullMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/WitherSkullMixin_API.java index 387579d5e62..ed258a11ad1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/WitherSkullMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/WitherSkullMixin_API.java @@ -22,9 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.hurtingprojectile; -import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -34,14 +33,12 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.projectile.WitherSkull.class) +@Mixin(net.minecraft.world.entity.projectile.hurtingprojectile.WitherSkull.class) public abstract class WitherSkullMixin_API extends AbstractHurtingProjectileMixin_API implements WitherSkull { @Override public void detonate() { - final Level.ExplosionInteraction mode = this.shadow$getCommandSenderWorld().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? Level.ExplosionInteraction.MOB : Level.ExplosionInteraction.NONE; - ((WitherSkullBridge) this).bridge$throwExplosionEventAndExplosde(this.shadow$getCommandSenderWorld(), (net.minecraft.world.entity.projectile.WitherSkull) (Object) this, - this.shadow$getX(), this.shadow$getY(), this.shadow$getZ(), 1.0F, false, mode); + ((WitherSkullBridge) this).bridge$wrappedExplode(this.shadow$getX(), this.shadow$getY(), this.shadow$getZ(), 1.0F, false, Level.ExplosionInteraction.MOB); this.shadow$discard(); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/windcharge/AbstractWindChargeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/windcharge/AbstractWindChargeMixin_API.java similarity index 69% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/windcharge/AbstractWindChargeMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/windcharge/AbstractWindChargeMixin_API.java index 360b9e610a0..72f44feb2e5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/windcharge/AbstractWindChargeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/windcharge/AbstractWindChargeMixin_API.java @@ -22,12 +22,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.windcharge; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.hurtingprojectile.windcharge; -import net.minecraft.world.entity.projectile.windcharge.AbstractWindCharge; +import net.minecraft.world.entity.projectile.hurtingprojectile.windcharge.AbstractWindCharge; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.api.entity.explosive.Explosive; import org.spongepowered.api.entity.projectile.windcharge.WindChargeLike; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.mixin.api.minecraft.world.entity.EntityMixin_API; @Mixin(AbstractWindCharge.class) -public abstract class AbstractWindChargeMixin_API implements WindChargeLike { +public abstract class AbstractWindChargeMixin_API extends EntityMixin_API implements WindChargeLike, Explosive { + + @Shadow protected abstract void shadow$explode(Vec3 var1); + + @Override + public void detonate() { + this.shadow$explode(this.shadow$position()); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/windcharge/BreezeWindChargeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/windcharge/BreezeWindChargeMixin_API.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/windcharge/BreezeWindChargeMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/windcharge/BreezeWindChargeMixin_API.java index f5313aff01b..5f42a87470d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/windcharge/BreezeWindChargeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/windcharge/BreezeWindChargeMixin_API.java @@ -22,11 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.windcharge; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.hurtingprojectile.windcharge; import org.spongepowered.api.entity.projectile.windcharge.BreezeWindCharge; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.projectile.windcharge.BreezeWindCharge.class) +@Mixin(net.minecraft.world.entity.projectile.hurtingprojectile.windcharge.BreezeWindCharge.class) public abstract class BreezeWindChargeMixin_API extends AbstractWindChargeMixin_API implements BreezeWindCharge { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/windcharge/WindChargeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/windcharge/WindChargeMixin_API.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/windcharge/WindChargeMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/windcharge/WindChargeMixin_API.java index 5988ba9f4d0..2f85850c616 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/windcharge/WindChargeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/hurtingprojectile/windcharge/WindChargeMixin_API.java @@ -22,11 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.windcharge; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.hurtingprojectile.windcharge; import org.spongepowered.api.entity.projectile.windcharge.WindCharge; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.projectile.windcharge.WindCharge.class) +@Mixin(net.minecraft.world.entity.projectile.hurtingprojectile.windcharge.WindCharge.class) public abstract class WindChargeMixin_API extends AbstractWindChargeMixin_API implements WindCharge { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/SnowballMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/SnowballMixin_API.java similarity index 92% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/SnowballMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/SnowballMixin_API.java index 48f40eef4aa..ff855ac10e6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/SnowballMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/SnowballMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.throwableitemprojectile; import org.spongepowered.api.entity.projectile.Snowball; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.projectile.Snowball.class) +@Mixin(net.minecraft.world.entity.projectile.throwableitemprojectile.Snowball.class) public abstract class SnowballMixin_API extends ThrowableItemProjectileMixin_API implements Snowball { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrowableItemProjectileMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrowableItemProjectileMixin_API.java similarity index 88% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrowableItemProjectileMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrowableItemProjectileMixin_API.java index 4c837849ac9..f7715097f66 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrowableItemProjectileMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrowableItemProjectileMixin_API.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.throwableitemprojectile; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.projectile.ThrowableItemProjectile; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.ThrowableProjectileMixin_API; import java.util.Set; -@Mixin(net.minecraft.world.entity.projectile.ThrowableItemProjectile.class) +@Mixin(net.minecraft.world.entity.projectile.throwableitemprojectile.ThrowableItemProjectile.class) public abstract class ThrowableItemProjectileMixin_API extends ThrowableProjectileMixin_API implements ThrowableItemProjectile { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownEggMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEggMixin_API.java similarity index 92% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownEggMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEggMixin_API.java index c0d3cf6129f..dc134e7f5a2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownEggMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEggMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.throwableitemprojectile; -import net.minecraft.world.entity.projectile.ThrownEgg; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownEgg; import org.spongepowered.api.entity.projectile.Egg; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownEnderpearlMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearlMixin_API.java similarity index 92% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownEnderpearlMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearlMixin_API.java index 534361f9473..d500aec68d8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownEnderpearlMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearlMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.throwableitemprojectile; -import net.minecraft.world.entity.projectile.ThrownEnderpearl; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownEnderpearl; import org.spongepowered.api.entity.projectile.EnderPearl; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownExperienceBottleMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownExperienceBottleMixin_API.java similarity index 92% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownExperienceBottleMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownExperienceBottleMixin_API.java index 1ec7c1b777e..c44afc19677 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownExperienceBottleMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownExperienceBottleMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.throwableitemprojectile; -import net.minecraft.world.entity.projectile.ThrownExperienceBottle; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownExperienceBottle; import org.spongepowered.api.entity.projectile.ExperienceBottle; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownPotionMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownPotionMixin_API.java similarity index 92% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownPotionMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownPotionMixin_API.java index 9df79854b24..fb503acbad3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/ThrownPotionMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/projectile/throwableitemprojectile/ThrownPotionMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile; +package org.spongepowered.common.mixin.api.minecraft.world.entity.projectile.throwableitemprojectile; -import net.minecraft.world.entity.projectile.ThrownPotion; +import net.minecraft.world.entity.projectile.throwableitemprojectile.AbstractThrownPotion; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.projectile.Potion; @@ -32,7 +32,7 @@ import java.util.Set; -@Mixin(ThrownPotion.class) +@Mixin(AbstractThrownPotion.class) public abstract class ThrownPotionMixin_API extends ThrowableItemProjectileMixin_API implements Potion { @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/raid/RaidMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/raid/RaidMixin_API.java index feb2b13a32d..6c66244d85c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/raid/RaidMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/raid/RaidMixin_API.java @@ -29,10 +29,8 @@ import net.minecraft.server.level.ServerBossEvent; import net.minecraft.world.entity.raid.Raid; import net.minecraft.world.entity.raid.Raider; -import net.minecraft.world.level.Level; import org.spongepowered.api.data.type.RaidStatus; import org.spongepowered.api.raid.RaidWave; -import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -53,16 +51,10 @@ public abstract class RaidMixin_API implements org.spongepowered.api.raid.Raid { @Shadow @Final private ServerBossEvent raidEvent; @Shadow private Raid.RaidStatus status; - @Shadow public abstract Level shadow$getLevel(); @Shadow public abstract float shadow$getHealthOfLivingRaiders(); @Shadow public abstract int shadow$getGroupsSpawned(); //@formatter:on - @Override - public ServerWorld world() { - return (ServerWorld) this.shadow$getLevel(); - } - @Override public BossBar bossBar() { return SpongeAdventure.asAdventure(this.raidEvent); diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/BoatMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/boat/AbstractBoatMixin_API.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/BoatMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/boat/AbstractBoatMixin_API.java index 2b7e87f7f52..aef5b5d68ff 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/BoatMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/boat/AbstractBoatMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle.boat; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; @@ -34,15 +34,14 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.vehicle.Boat.class) +@Mixin(net.minecraft.world.entity.vehicle.boat.AbstractBoat.class) @Implements(@Interface(iface = Boat.class, prefix = "apiBoat$")) -public abstract class BoatMixin_API extends EntityMixin_API implements Boat { +public abstract class AbstractBoatMixin_API extends EntityMixin_API implements Boat { @Override protected Set> api$getVanillaValues() { final Set> values = super.api$getVanillaValues(); - values.add(this.requireValue(Keys.BOAT_TYPE).asImmutable()); values.add(this.requireValue(Keys.CAN_MOVE_ON_LAND).asImmutable()); values.add(this.requireValue(Keys.IS_IN_WATER).asImmutable()); values.add(this.requireValue(Keys.MAX_SPEED).asImmutable()); diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/ChestBoatMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/boat/AbstractChestBoatMixin_API.java similarity index 89% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/ChestBoatMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/boat/AbstractChestBoatMixin_API.java index 1f1abb933dd..d8c51298e93 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/ChestBoatMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/boat/AbstractChestBoatMixin_API.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle.boat; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.vehicle.ChestBoat; @@ -30,8 +30,8 @@ import java.util.Set; -@Mixin(net.minecraft.world.entity.vehicle.ChestBoat.class) -public abstract class ChestBoatMixin_API extends BoatMixin_API implements ChestBoat { +@Mixin(net.minecraft.world.entity.vehicle.boat.AbstractChestBoat.class) +public abstract class AbstractChestBoatMixin_API extends AbstractBoatMixin_API implements ChestBoat { @Override protected Set> api$getVanillaValues() { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/AbstractMinecartContainerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainerMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/AbstractMinecartContainerMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainerMixin_API.java index be06c2e6cb9..b112b1914ad 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/AbstractMinecartContainerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainerMixin_API.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.AbstractMinecartContainer; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecartContainer; import org.spongepowered.api.entity.vehicle.minecart.carrier.CarrierMinecart; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/AbstractMinecartMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/AbstractMinecartMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/AbstractMinecartMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/AbstractMinecartMixin_API.java index b4f4074fb83..9a2207db953 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/AbstractMinecartMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/AbstractMinecartMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.AbstractMinecart; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecart; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.vehicle.minecart.MinecartLike; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartChestMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartChestMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartChestMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartChestMixin_API.java index b930cd56408..2bf826b2468 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartChestMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartChestMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.MinecartChest; +import net.minecraft.world.entity.vehicle.minecart.MinecartChest; import org.spongepowered.api.entity.vehicle.minecart.carrier.ChestMinecart; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartCommandBlockMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartCommandBlockMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartCommandBlockMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartCommandBlockMixin_API.java index a53afd2da02..52e3f4c9fb1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartCommandBlockMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartCommandBlockMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.MinecartCommandBlock; +import net.minecraft.world.entity.vehicle.minecart.MinecartCommandBlock; import net.minecraft.world.level.BaseCommandBlock; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartFurnaceMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartFurnaceMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartFurnaceMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartFurnaceMixin_API.java index ddb17dd9cc3..b384d470155 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartFurnaceMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartFurnaceMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.MinecartFurnace; +import net.minecraft.world.entity.vehicle.minecart.MinecartFurnace; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.vehicle.minecart.FurnaceMinecart; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartHopperMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartHopperMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartHopperMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartHopperMixin_API.java index 0fcceb8049a..4f5e4b3389a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartHopperMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartHopperMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.MinecartHopper; +import net.minecraft.world.entity.vehicle.minecart.MinecartHopper; import org.spongepowered.api.entity.vehicle.minecart.carrier.HopperMinecart; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartMixin_API.java index aa186ec4187..db76e281ef2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartMixin_API.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle.minecart; import org.spongepowered.api.entity.vehicle.minecart.Minecart; import org.spongepowered.asm.mixin.Mixin; -@Mixin(net.minecraft.world.entity.vehicle.Minecart.class) +@Mixin(net.minecraft.world.entity.vehicle.minecart.Minecart.class) public abstract class MinecartMixin_API extends AbstractMinecartMixin_API implements Minecart { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartSpawnerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartSpawnerMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartSpawnerMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartSpawnerMixin_API.java index 5c9f7acbdcf..62026b7cc23 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartSpawnerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartSpawnerMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.MinecartSpawner; +import net.minecraft.world.entity.vehicle.minecart.MinecartSpawner; import org.spongepowered.api.entity.vehicle.minecart.SpawnerMinecart; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartTNTMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartTNTMixin_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartTNTMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartTNTMixin_API.java index 5ed1652b6e3..effee95c015 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/MinecartTNTMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/minecart/MinecartTNTMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.MinecartTNT; +import net.minecraft.world.entity.vehicle.minecart.MinecartTNT; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.entity.vehicle.minecart.TNTMinecart; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ArmorMaterialMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ArmorMaterialMixin_API.java index 4a003b8b2b3..144190b7a3b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ArmorMaterialMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ArmorMaterialMixin_API.java @@ -24,30 +24,30 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.item; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; import org.spongepowered.api.data.type.ArmorMaterial; -import org.spongepowered.api.item.recipe.crafting.Ingredient; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Implements; import org.spongepowered.asm.mixin.Interface; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.common.item.util.ItemStackUtil; -import java.util.Optional; -import java.util.function.Supplier; +import java.util.function.Predicate; -@Mixin(net.minecraft.world.item.ArmorMaterial.class) +@Mixin(net.minecraft.world.item.equipment.ArmorMaterial.class) @Implements(@Interface(iface = ArmorMaterial.class, prefix = "armorMaterial$")) public abstract class ArmorMaterialMixin_API implements ArmorMaterial { // @formatter:off - @Shadow @Final private Supplier repairIngredient; - + @Shadow @Final private TagKey repairIngredient; // @formatter:on - @Override - public Optional repairIngredient() { - final net.minecraft.world.item.crafting.Ingredient repairMaterial = this.repairIngredient.get(); - return Optional.ofNullable(((Ingredient) (Object) repairMaterial)); + @Unique + public Predicate armorMaterial$repairIngredient() { + return (spongeStack) -> ItemStackUtil.toNative(spongeStack).is(this.repairIngredient); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemCooldownsMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemCooldownsMixin_API.java index 023fb869015..ae0caccdded 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemCooldownsMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemCooldownsMixin_API.java @@ -25,15 +25,18 @@ package org.spongepowered.common.mixin.api.minecraft.world.item; -import net.minecraft.world.item.Item; +import net.minecraft.resources.Identifier; +import net.minecraft.util.Mth; import net.minecraft.world.item.ItemCooldowns; -import org.spongepowered.api.item.ItemType; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.util.Ticks; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.accessor.world.item.ItemCooldowns_CooldownInstanceAccessor; import org.spongepowered.common.bridge.world.item.ItemCooldownsBridge; +import org.spongepowered.common.item.util.ItemStackUtil; import org.spongepowered.common.util.SpongeTicks; import java.util.Map; @@ -45,25 +48,55 @@ public abstract class ItemCooldownsMixin_API implements org.spongepowered.api.entity.living.player.CooldownTracker { // @formatter:off - @Shadow @Final private Map cooldowns; @Shadow private int tickCount; - @Shadow public abstract boolean shadow$isOnCooldown(Item itemIn); - @Shadow public abstract float shadow$getCooldownPercent(Item itemIn, float partialTicks); - @Shadow public abstract void shadow$addCooldown(final Item item, final int ticks); + @Shadow public abstract boolean shadow$isOnCooldown(ItemStack stack); + @Shadow public abstract float shadow$getCooldownPercent(ItemStack stack, float partialTicks); + @Shadow public abstract void shadow$addCooldown(final ItemStack stack, final int ticks); + @Shadow public abstract void shadow$addCooldown(final Identifier group, final int ticks); + @Shadow @Final private Map cooldowns; + // @formatter:on + + @Shadow public abstract Identifier getCooldownGroup(final ItemStack $$0); + + @Override + public boolean hasCooldown(final org.spongepowered.api.item.inventory.ItemStack stack) { + Objects.requireNonNull(stack, "ItemStack cannot be null!"); + return this.shadow$isOnCooldown(ItemStackUtil.toNative(stack)); + } + + /** + * see {@link ItemCooldowns#getCooldownPercent(ItemStack, float)} + */ @Override - public boolean hasCooldown(final ItemType type) { - Objects.requireNonNull(type, "Item type cannot be null!"); - return this.shadow$isOnCooldown((Item) type); + public boolean hasCooldown(final ResourceKey group) { + Objects.requireNonNull(group, "group cannot be null!"); + return impl$getCooldownPercent((Object) group) > 0; + } + + private float impl$getCooldownPercent(final Object group) { + final var cooldown = this.cooldowns.get((Identifier) group); + if (cooldown != null) { + float $$4 = (float)(cooldown.accessor$endTime() - cooldown.accessor$startTime()); + float $$5 = (float)cooldown.accessor$endTime() - ((float)this.tickCount); + return Mth.clamp($$5 / $$4, 0.0F, 1.0F); + } + return 0; } @Override - public Optional cooldown(final ItemType type) { - Objects.requireNonNull(type, "Item type cannot be null!"); + public Optional cooldown(final org.spongepowered.api.item.inventory.ItemStack stack) { + Objects.requireNonNull(stack, "ItemStack cannot be null!"); + final var group = this.getCooldownGroup(ItemStackUtil.toNative(stack)); + return this.cooldown((ResourceKey) (Object) group); + } - final ItemCooldowns_CooldownInstanceAccessor cooldown = (ItemCooldowns_CooldownInstanceAccessor) this.cooldowns.get((Item) type); + @Override + public Optional cooldown(final ResourceKey group) { + Objects.requireNonNull(group, "group cannot be null!"); + final var cooldown = this.cooldowns.get(group); if (cooldown != null) { final int remainingCooldown = cooldown.accessor$endTime() - this.tickCount; @@ -75,22 +108,32 @@ public Optional cooldown(final ItemType type) { } @Override - public boolean setCooldown(final ItemType type, final Ticks ticks) { - Objects.requireNonNull(type, "Item type cannot be null!"); - this.shadow$addCooldown((Item) type, SpongeTicks.toSaturatedIntOrInfinite(ticks)); + public boolean setCooldown(final org.spongepowered.api.item.inventory.ItemStack stack, final Ticks ticks) { + Objects.requireNonNull(stack, "ItemStack cannot be null!"); + this.shadow$addCooldown(ItemStackUtil.toNative(stack), SpongeTicks.toSaturatedIntOrInfinite(ticks)); return ((ItemCooldownsBridge) this).bridge$getSetCooldownResult(); } + @Override + public boolean setCooldown(final ResourceKey group, final Ticks ticks) { + Objects.requireNonNull(group, "group cannot be null!"); + this.shadow$addCooldown((Identifier) (Object) group, SpongeTicks.toSaturatedIntOrInfinite(ticks)); + return ((ItemCooldownsBridge) this).bridge$getSetCooldownResult(); + } + @Override + public boolean resetCooldown(final org.spongepowered.api.item.inventory.ItemStack stack) { + return this.setCooldown(stack, Ticks.zero()); + } @Override - public boolean resetCooldown(final ItemType type) { - return this.setCooldown(type, Ticks.zero()); + public boolean resetCooldown(final ResourceKey group) { + return this.setCooldown(group, Ticks.zero()); } @Override - public OptionalDouble fractionRemaining(final ItemType type) { - Objects.requireNonNull(type, "Item type cannot be null!"); - final float cooldown = this.shadow$getCooldownPercent((Item) type, 0); + public OptionalDouble fractionRemaining(final org.spongepowered.api.item.inventory.ItemStack stack) { + Objects.requireNonNull(stack, "ItemStack cannot be null!"); + final float cooldown = this.shadow$getCooldownPercent(ItemStackUtil.toNative(stack), 0); if (cooldown > 0.0F) { return OptionalDouble.of(cooldown); @@ -98,5 +141,13 @@ public OptionalDouble fractionRemaining(final ItemType type) { return OptionalDouble.empty(); } - + @Override + public OptionalDouble fractionRemaining(final ResourceKey group) { + Objects.requireNonNull(group, "group cannot be null!"); + final float cooldown = this.impl$getCooldownPercent(group); + if (cooldown > 0.0F) { + return OptionalDouble.of(cooldown); + } + return OptionalDouble.empty(); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemMixin_API.java index 557d19c5177..e04a721795b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemMixin_API.java @@ -25,43 +25,36 @@ package org.spongepowered.common.mixin.api.minecraft.world.item; import net.kyori.adventure.text.Component; -import net.minecraft.core.Holder; import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponents; -import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStackTemplate; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.block.BlockType; import org.spongepowered.api.item.ItemRarity; import org.spongepowered.api.item.ItemType; -import org.spongepowered.api.registry.DefaultedRegistryType; -import org.spongepowered.api.registry.RegistryTypes; -import org.spongepowered.api.tag.Tag; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.data.holder.SpongeImmutableDataHolder; import java.util.Arrays; -import java.util.Collection; import java.util.Optional; import java.util.function.Supplier; -import java.util.stream.Collectors; @Mixin(Item.class) public abstract class ItemMixin_API implements ItemType, SpongeImmutableDataHolder { // @formatter:off - @Shadow @Final private Holder.Reference builtInRegistryHolder; - @Shadow @Final private DataComponentMap components; + @Shadow @Final @Nullable private ItemStackTemplate craftingRemainingItem; @Shadow public abstract String shadow$getDescriptionId(); - @Shadow @Nullable public abstract Item shadow$getCraftingRemainingItem(); - + @Shadow public abstract DataComponentMap shadow$components(); @Shadow public abstract int shadow$getDefaultMaxStackSize(); // @formatter:on + @Nullable protected BlockType api$blockType = null; @Override @@ -76,7 +69,7 @@ public int maxStackQuantity() { @Override public ItemRarity rarity() { - return (ItemRarity) (Object) this.components.get(DataComponents.RARITY); + return (ItemRarity) (Object) this.shadow$components().get(DataComponents.RARITY); } @Override @@ -94,24 +87,11 @@ public boolean isAnyOf(ItemType... types) { return Arrays.stream(types).anyMatch(type -> type == this); } - @Override - public DefaultedRegistryType registryType() { - return RegistryTypes.ITEM_TYPE; - } - - @Override - public Collection> tags() { - return this.registryType().get().tags().filter(this::is).collect(Collectors.toSet()); - } - - @Override - public boolean is(Tag tag) { - return this.builtInRegistryHolder.is((TagKey) (Object) tag); - } - @Override public Optional container() { - final Item craftingRemainingItem = this.shadow$getCraftingRemainingItem(); - return Optional.ofNullable((ItemType) craftingRemainingItem); + if (this.craftingRemainingItem == null) { + return Optional.empty(); + } + return Optional.of((ItemType) this.craftingRemainingItem.item().value()); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemStackMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemStackMixin_API.java index cdc50f39d4d..b7aa8d39f22 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemStackMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemStackMixin_API.java @@ -75,7 +75,6 @@ public abstract class ItemStackMixin_API implements SerializableDataHolder.Mutab // @formatter:off @Shadow public abstract int shadow$getCount(); @Shadow public abstract void shadow$setCount(int size); // Do not use field directly as Minecraft tracks the empty state - @Shadow public abstract int shadow$getMaxStackSize(); @Shadow public abstract boolean shadow$isEmpty(); @Shadow public abstract net.minecraft.world.item.ItemStack shadow$copy(); @Shadow public abstract Item shadow$getItem(); @@ -211,6 +210,6 @@ public Component asComponent() { } public int itemStack$maxStackQuantity() { - return this.shadow$getMaxStackSize(); + return ((net.minecraft.world.item.ItemStack) (Object) this).getMaxStackSize(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/JukeboxSongMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/JukeboxSongMixin_API.java index 7b7fa4a636a..1ac5dd00437 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/JukeboxSongMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/JukeboxSongMixin_API.java @@ -40,6 +40,6 @@ public abstract class JukeboxSongMixin_API implements MusicDisc { @Override public SoundType sound() { - return (SoundType) this.soundEvent.value(); + return (SoundType) (Object) this.soundEvent.value(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ToolMaterialMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ToolMaterialMixin_API.java new file mode 100644 index 00000000000..2a0904d32da --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ToolMaterialMixin_API.java @@ -0,0 +1,52 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.item; + +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import org.spongepowered.api.data.type.ItemTier; +import org.spongepowered.api.item.recipe.crafting.Ingredient; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.item.recipe.ingredient.SpongeIngredient; + +@Mixin(net.minecraft.world.item.ToolMaterial.class) +public abstract class ToolMaterialMixin_API implements ItemTier { + + // @formatter:off + @Shadow @Final private TagKey repairItems; + // @formatter:on + + @Override + public Ingredient repairIngredient() { + Registry holder = SpongeCommon.vanillaRegistry(Registries.ITEM); + final var items = holder.getOrThrow(this.repairItems); + return (Ingredient) (Object) SpongeIngredient.of(items); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_DamageReductionMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_DamageReductionMixin_API.java new file mode 100644 index 00000000000..248bc1f1910 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_DamageReductionMixin_API.java @@ -0,0 +1,84 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.item.component; + +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.world.item.component.BlocksAttacks; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.event.cause.entity.damage.DamageType; +import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; +import org.spongepowered.asm.mixin.*; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +@Mixin(BlocksAttacks.DamageReduction.class) +@Implements({ + @Interface(iface = ShieldDamageReduction.class, prefix = "shielddamagereduction$"), + @Interface(iface = ShieldDamageReduction.MultiplyAdd.class, prefix = "shielddamagereductionmultiplyadd$") +}) +public abstract class BlocksAttacks_DamageReductionMixin_API implements ShieldDamageReduction, ShieldDamageReduction.MultiplyAdd { + + @Shadow @Final private Optional> type; + @Shadow @Final private float base; + @Shadow @Final private float factor; + @Shadow @Final private float horizontalBlockingAngle; + + @Shadow public abstract float shadow$resolve(net.minecraft.world.damagesource.DamageSource $$0, float $$1, double $$2); + + @Override + public MultiplyAdd configuration() { + return this; + } + + public double shielddamagereduction$resolve(final DamageSource source, final double damage, final double angle) { + return this.shadow$resolve((net.minecraft.world.damagesource.DamageSource) source, (float) damage, angle); + } + + @Override + public Optional> damageTypes() { + return this.type.map(set -> set.stream() + .map(Holder::value) + .map(DamageType.class::cast) + .collect(Collectors.toSet())); + } + + public double shielddamagereductionmultiplyadd$horizontalBlockingAngle() { + return this.horizontalBlockingAngle; + } + + @Override + public double constantReduction() { + return this.base; + } + + @Override + public double fractionalReduction() { + return this.factor; + } + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_ItemDamageFunction_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_ItemDamageFunction_API.java new file mode 100644 index 00000000000..558458c2460 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_ItemDamageFunction_API.java @@ -0,0 +1,67 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.item.component; + +import net.minecraft.world.item.component.BlocksAttacks; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(BlocksAttacks.ItemDamageFunction.class) +public abstract class BlocksAttacks_ItemDamageFunction_API implements ShieldItemDamageFunction.MultiplyAdd, ShieldItemDamageFunction { + + @Shadow @Final private float threshold; + @Shadow @Final private float base; + @Shadow @Final private float factor; + + @Shadow public abstract int shadow$apply(float $$0); + + @Override + public MultiplyAdd configuration() { + return this; + } + + @Override + public double resolve(final double damage) { + return this.shadow$apply((float) damage); + } + + @Override + public double minAttackDamage() { + return this.threshold; + } + + @Override + public double constantDamage() { + return this.base; + } + + @Override + public double fractionalDamage() { + return this.factor; + } + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/ItemAttributeModifiers_EntryMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/ItemAttributeModifiers_EntryMixin_API.java new file mode 100644 index 00000000000..14ad842499e --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/ItemAttributeModifiers_EntryMixin_API.java @@ -0,0 +1,64 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.item.component; + +import net.minecraft.core.Holder; +import net.minecraft.world.entity.EquipmentSlotGroup; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.item.component.ItemAttributeModifiers; +import org.spongepowered.api.entity.attribute.AttributeModifier; +import org.spongepowered.api.entity.attribute.ItemAttribute; +import org.spongepowered.api.entity.attribute.type.AttributeType; +import org.spongepowered.api.item.inventory.equipment.EquipmentCondition; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ItemAttributeModifiers.Entry.class) +@Implements(@Interface(iface = ItemAttribute.class, prefix = "attribute$")) +public abstract class ItemAttributeModifiers_EntryMixin_API implements ItemAttribute { + + @Shadow @Final private Holder attribute; + @Shadow @Final private net.minecraft.world.entity.ai.attributes.AttributeModifier modifier; + @Shadow @Final private EquipmentSlotGroup slot; + + @Override + public AttributeType type() { + return (AttributeType) this.attribute.value(); + } + + @Intrinsic + public AttributeModifier attribute$modifier() { + return (AttributeModifier) (Object) this.modifier; + } + + @Override + public EquipmentCondition condition() { + return (EquipmentCondition) (Object) this.slot; + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ApplyStatusEffectsConsumeEffectMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ApplyStatusEffectsConsumeEffectMixin_API.java new file mode 100644 index 00000000000..1a2c5a5b2c4 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ApplyStatusEffectsConsumeEffectMixin_API.java @@ -0,0 +1,56 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.item.consume_effects; + +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect; +import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.api.effect.potion.PotionEffect; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.List; + +@Mixin(ApplyStatusEffectsConsumeEffect.class) +@Implements(@Interface(iface = ItemAction.ApplyEffects.class, prefix = "consumeEffect$")) +public abstract class ApplyStatusEffectsConsumeEffectMixin_API implements ItemAction.ApplyEffects { + + @Shadow @Final private float probability; + @Shadow @Final private List effects; + + @Override + public double chance() { + return this.probability; + } + + @Intrinsic + public List consumeEffect$effects() { + return (List) this.effects; + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffectMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffectMixin_API.java new file mode 100644 index 00000000000..ffb15a2514d --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffectMixin_API.java @@ -0,0 +1,33 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.item.consume_effects; + +import net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect; +import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ClearAllStatusEffectsConsumeEffect.class) +public abstract class ClearAllStatusEffectsConsumeEffectMixin_API implements ItemAction.ClearEffects { +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ConsumeEffectMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ConsumeEffectMixin_API.java new file mode 100644 index 00000000000..b6157338515 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ConsumeEffectMixin_API.java @@ -0,0 +1,60 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.item.consume_effects; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.consume_effects.ConsumeEffect; +import net.minecraft.world.level.Level; +import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.api.data.type.ItemActionType; +import org.spongepowered.api.entity.living.Living; +import org.spongepowered.api.item.inventory.ItemStackLike; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.item.util.ItemStackUtil; + +import java.util.Objects; + +@Mixin(ConsumeEffect.class) +public interface ConsumeEffectMixin_API extends ItemAction { + + @Shadow ConsumeEffect.Type shadow$getType(); + @Shadow boolean shadow$apply(Level level, ItemStack itemStack, LivingEntity livingEntity); + + @Override + default ItemActionType type() { + return (ItemActionType) (Object) this.shadow$getType(); + } + + @Override + default boolean apply(final Living entity, final ItemStackLike stack) { + Objects.requireNonNull(entity, "entity"); + return this.shadow$apply( + (Level) entity.world(), + ItemStackUtil.fromLikeToNativeCopy(stack), + (LivingEntity) entity); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ConsumeEffect_TypeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ConsumeEffect_TypeMixin_API.java new file mode 100644 index 00000000000..31e206c0c91 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/ConsumeEffect_TypeMixin_API.java @@ -0,0 +1,33 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.item.consume_effects; + +import net.minecraft.world.item.consume_effects.ConsumeEffect; +import org.spongepowered.api.data.type.ItemActionType; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ConsumeEffect.Type.class) +public abstract class ConsumeEffect_TypeMixin_API implements ItemActionType { +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartCommandBlock_MinecartCommandBaseMixin.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/PlaySoundConsumeEffectMixin_API.java similarity index 67% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartCommandBlock_MinecartCommandBaseMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/PlaySoundConsumeEffectMixin_API.java index b1892560878..4e3f681d634 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartCommandBlock_MinecartCommandBaseMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/PlaySoundConsumeEffectMixin_API.java @@ -22,25 +22,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.vehicle; +package org.spongepowered.common.mixin.api.minecraft.world.item.consume_effects; -import net.minecraft.world.entity.vehicle.MinecartCommandBlock; -import org.spongepowered.api.service.permission.Subject; -import org.spongepowered.api.service.permission.SubjectProxy; +import net.minecraft.core.Holder; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.item.consume_effects.PlaySoundConsumeEffect; +import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.api.effect.sound.SoundType; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.mixin.core.world.BaseCommandBlockMixin; -@Mixin(MinecartCommandBlock.MinecartCommandBase.class) -public abstract class MinecartCommandBlock_MinecartCommandBaseMixin extends BaseCommandBlockMixin implements SubjectProxy { +@Mixin(PlaySoundConsumeEffect.class) +public abstract class PlaySoundConsumeEffectMixin_API implements ItemAction.PlaySound { - // @formatter:off - @Shadow @Final MinecartCommandBlock this$0; - // @formatter:on + @Shadow @Final private Holder sound; @Override - public Subject subject() { - return (Subject) this.this$0; + public SoundType soundType() { + return (SoundType) (Object) this.sound.value(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/MobSpawnSettings_SpawnerDataMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/RemoveStatusEffectsConsumeEffectMixin_API.java similarity index 63% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/MobSpawnSettings_SpawnerDataMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/RemoveStatusEffectsConsumeEffectMixin_API.java index 1e0b11b06b5..1aad96d28f3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/MobSpawnSettings_SpawnerDataMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/RemoveStatusEffectsConsumeEffectMixin_API.java @@ -22,36 +22,28 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.level.biome; +package org.spongepowered.common.mixin.api.minecraft.world.item.consume_effects; -import net.minecraft.world.level.biome.MobSpawnSettings; -import org.spongepowered.api.entity.EntityType; -import org.spongepowered.api.world.biome.spawner.NaturalSpawner; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect; +import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.api.effect.potion.PotionEffectType; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -@Mixin(MobSpawnSettings.SpawnerData.class) -public abstract class MobSpawnSettings_SpawnerDataMixin_API implements NaturalSpawner { +import java.util.Set; +import java.util.stream.Collectors; - // @formatter:off - @Shadow @Final public net.minecraft.world.entity.EntityType type; - @Shadow @Final public int minCount; - @Shadow @Final public int maxCount; - // @formatter:on +@Mixin(RemoveStatusEffectsConsumeEffect.class) +public abstract class RemoveStatusEffectsConsumeEffectMixin_API implements ItemAction.RemoveEffects { - @Override - public EntityType type() { - return (EntityType) this.type; - } - - @Override - public int min() { - return this.minCount; - } + @Shadow @Final private HolderSet effects; @Override - public int max() { - return this.maxCount; + public Set effectTypes() { + return this.effects.stream().map(Holder::value).map(PotionEffectType.class::cast).collect(Collectors.toSet()); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/TiersMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffectMixin_API.java similarity index 69% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/TiersMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffectMixin_API.java index f737fd964b3..b1a9f2bd27d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/TiersMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffectMixin_API.java @@ -22,22 +22,21 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.item; +package org.spongepowered.common.mixin.api.minecraft.world.item.consume_effects; -import net.minecraft.world.item.crafting.Ingredient; -import org.spongepowered.api.data.type.ItemTier; +import net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect; +import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -@Mixin(net.minecraft.world.item.Tiers.class) -public abstract class TiersMixin_API implements ItemTier { +@Mixin(TeleportRandomlyConsumeEffect.class) +public abstract class TeleportRandomlyConsumeEffectMixin_API implements ItemAction.TeleportRandomly { - // @formatter:off - @Shadow public abstract Ingredient shadow$getRepairIngredient(); - // @formatter:on + @Shadow @Final private float diameter; @Override - public org.spongepowered.api.item.recipe.crafting.Ingredient repairIngredient() { - return (org.spongepowered.api.item.recipe.crafting.Ingredient) (Object) this.shadow$getRepairIngredient(); + public double distance() { + return this.diameter / 2; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/AbstractCookingRecipeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/AbstractCookingRecipeMixin_API.java index 9075a7f4354..22499c29de5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/AbstractCookingRecipeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/AbstractCookingRecipeMixin_API.java @@ -25,12 +25,10 @@ package org.spongepowered.common.mixin.api.minecraft.world.item.crafting; import net.minecraft.world.item.crafting.AbstractCookingRecipe; -import net.minecraft.world.item.crafting.Ingredient; import org.spongepowered.api.item.inventory.ItemStackLike; import org.spongepowered.api.item.recipe.cooking.CookingRecipe; import org.spongepowered.api.item.recipe.cooking.CookingResult; import org.spongepowered.api.util.Ticks; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.item.recipe.ingredient.IngredientUtil; @@ -40,39 +38,33 @@ import java.util.Optional; @Mixin(AbstractCookingRecipe.class) -public abstract class AbstractCookingRecipeMixin_API implements CookingRecipe { +public abstract class AbstractCookingRecipeMixin_API extends SingleItemRecipeMixin_API implements CookingRecipe { // @formatter:off - @Shadow @Final protected Ingredient ingredient; - @Shadow public abstract float shadow$getExperience(); - @Shadow public abstract int shadow$getCookingTime(); + @Shadow public abstract float shadow$experience(); + @Shadow public abstract int shadow$cookingTime(); // @formatter:on @Override public org.spongepowered.api.item.recipe.crafting.Ingredient ingredient() { - return IngredientUtil.fromNative(this.ingredient); + return IngredientUtil.fromNative(this.shadow$input()); } @Override public boolean isValid(final ItemStackLike ingredient) { - return this.ingredient.test(ItemStackUtil.fromLikeToNative(ingredient)); + return this.shadow$input().test(ItemStackUtil.fromLikeToNative(ingredient)); } @Override public Optional result(final ItemStackLike ingredient) { if (this.isValid(ingredient)) { - return Optional.of(new CookingResult(this.exemplaryResult(), this.shadow$getExperience())); + return Optional.of(new CookingResult((ItemStackLike) this.exemplaryResult(), this.shadow$experience())); } return Optional.empty(); } @Override public Ticks cookingTime() { - return SpongeTicks.ticksOrInfinite(this.shadow$getCookingTime()); - } - - @Override - public float experience() { - return this.shadow$getExperience(); + return SpongeTicks.ticksOrInfinite(this.shadow$cookingTime()); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/CraftingRecipeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/CraftingRecipeMixin_API.java index c5c25ee36c3..c11308717b6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/CraftingRecipeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/CraftingRecipeMixin_API.java @@ -24,13 +24,20 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.item.crafting; +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.CraftingInput; import net.minecraft.world.item.crafting.ShapedRecipe; import net.minecraft.world.item.crafting.ShapelessRecipe; +import org.spongepowered.api.item.inventory.ItemStackSnapshot; import org.spongepowered.api.item.recipe.RecipeType; import org.spongepowered.api.item.recipe.crafting.CraftingRecipe; +import org.spongepowered.api.item.recipe.crafting.RecipeInput; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.item.util.ItemStackUtil; +import java.util.List; import java.util.Optional; @Mixin(net.minecraft.world.item.crafting.CraftingRecipe.class) @@ -38,8 +45,16 @@ public interface CraftingRecipeMixin_API extends CraftingRecipe { // @formatter:off @Shadow net.minecraft.world.item.crafting.RecipeType shadow$getType(); + @Shadow NonNullList shadow$getRemainingItems(CraftingInput $$0); // @formatter:on + @Override + default List remainingItems(RecipeInput.Crafting inventory) { + return this.shadow$getRemainingItems((CraftingInput) inventory).stream() + .map(ItemStackUtil::snapshotOf) + .toList(); + } + default RecipeType type() { return (RecipeType) this.shadow$getType(); } @@ -47,10 +62,10 @@ default RecipeType type() { default Optional group() { String group = ""; if (this instanceof ShapedRecipe sr) { - group = sr.getGroup(); + group = sr.group(); } if (this instanceof ShapelessRecipe sr) { - group = sr.getGroup(); + group = sr.group(); } if (group.isEmpty()) { return Optional.empty(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/IngredientMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/IngredientMixin_API.java index 34d48fb56a0..096b1ee5784 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/IngredientMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/IngredientMixin_API.java @@ -24,9 +24,12 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.item.crafting; +import net.minecraft.core.Holder; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.item.ItemType; import org.spongepowered.api.item.inventory.ItemStackLike; import org.spongepowered.asm.mixin.Implements; import org.spongepowered.asm.mixin.Interface; @@ -35,22 +38,20 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.item.util.ItemStackUtil; -import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; +import java.util.stream.Stream; @Mixin(Ingredient.class) @Implements(@Interface(iface = org.spongepowered.api.item.recipe.crafting.Ingredient.class, prefix = "ingredient$", remap = Remap.NONE)) public abstract class IngredientMixin_API { // @formatter:off - @Shadow public abstract ItemStack[] shadow$getItems(); @Shadow public abstract boolean shadow$test(@Nullable ItemStack p_test_1_); + @Shadow public abstract Stream> shadow$items(); // @formatter:on - - public List ingredient$displayedItems() { - return Arrays.stream(this.shadow$getItems()).map(ItemStackUtil::snapshotOf).collect(Collectors.toList()); + public List ingredient$displayedItems() { + return this.shadow$items().map((holder) -> (ItemType) holder.value()).toList(); } public boolean ingredient$test(final ItemStackLike itemStack) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/RecipeManagerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/RecipeManagerMixin_API.java index 84be7c7913c..6d3cbc4d74f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/RecipeManagerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/RecipeManagerMixin_API.java @@ -24,13 +24,13 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.item.crafting; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.Container; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.item.crafting.RecipeInput; import net.minecraft.world.item.crafting.RecipeManager.CachedCheck; +import net.minecraft.world.item.crafting.RecipeMap; import net.minecraft.world.item.crafting.SingleRecipeInput; -import net.minecraft.world.level.Level; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.item.inventory.ItemStackLike; import org.spongepowered.api.item.recipe.Recipe; @@ -40,6 +40,7 @@ import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.accessor.world.item.crafting.RecipeMapAccessor; import org.spongepowered.common.inventory.util.InventoryUtil; import org.spongepowered.common.item.util.ItemStackUtil; @@ -52,18 +53,25 @@ public abstract class RecipeManagerMixin_API implements RecipeManager { // @formatter:off - @Shadow public abstract Optional> shadow$byKey(ResourceLocation recipeId); - @Shadow protected abstract > Collection> shadow$byType(net.minecraft.world.item.crafting.RecipeType recipeTypeIn); @Shadow public abstract Collection> shadow$getRecipes(); @Shadow public abstract > Optional> shadow$getRecipeFor(net.minecraft.world.item.crafting.RecipeType recipeTypeIn, I inventoryIn, net.minecraft.world.level.Level worldIn); // @formatter:on + @Shadow + private RecipeMap recipes; + @SuppressWarnings({"DataFlowIssue", "unchecked"}) @Override public Optional> byKey(final ResourceKey key) { Objects.requireNonNull(key); - return this.shadow$byKey((ResourceLocation) (Object) key).map(RecipeHolder::value).map(Recipe.class::cast); + // TODO - figure out how to do this better + for (var entry : ((RecipeMapAccessor) this.recipes).accessor$byKey().entrySet()) { + if (entry.getKey().identifier().equals(key)) { + return Optional.of(entry.getValue()).map(RecipeHolder::value).map(Recipe.class::cast); + } + } + return Optional.empty(); } @Override @@ -76,7 +84,7 @@ public Collection> all() { @SuppressWarnings(value = {"unchecked", "rawtypes"}) public > Collection allOfType(final RecipeType type) { Objects.requireNonNull(type); - return this.shadow$byType((net.minecraft.world.item.crafting.RecipeType)type).stream().map(h -> ((RecipeHolder)h).value()).toList(); + return this.recipes.byType((net.minecraft.world.item.crafting.RecipeType)type).stream().map(h -> ((RecipeHolder)h).value()).toList(); } @Override @@ -100,12 +108,12 @@ public RecipeManagerMixin_API.impl$getRecipe(level, checker, in)); } - private static > Optional> impl$getRecipe(final Level level, final CachedCheck checker, final I input) { + private static > Optional> impl$getRecipe(final ServerLevel level, final CachedCheck checker, final I input) { return checker.getRecipeFor(input, level).map(RecipeHolder::value).map(Recipe.class::cast); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/RecipeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/RecipeMixin_API.java index af01bd79ede..b982e6454d4 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/RecipeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/RecipeMixin_API.java @@ -24,69 +24,74 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.item.crafting; -import static org.spongepowered.common.inventory.util.InventoryUtil.toCraftingInputOrThrow; - -import net.minecraft.core.HolderLookup; -import net.minecraft.core.NonNullList; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeInput; +import net.minecraft.world.item.crafting.display.RecipeDisplay; +import net.minecraft.world.item.crafting.display.SlotDisplay; import org.checkerframework.checker.nullness.qual.NonNull; +import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.item.inventory.ItemStackSnapshot; import org.spongepowered.api.item.recipe.Recipe; import org.spongepowered.api.item.recipe.RecipeType; -import org.spongepowered.api.item.recipe.crafting.Ingredient; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.item.recipe.ingredient.IngredientUtil; +import org.spongepowered.common.inventory.util.InventoryUtil; +import org.spongepowered.common.item.recipe.crafting.RecipeUtil; import org.spongepowered.common.item.util.ItemStackUtil; +import org.spongepowered.common.util.DataPackUtil; import java.util.List; -import java.util.stream.Collectors; +import java.util.Optional; +import java.util.function.Predicate; @Mixin(net.minecraft.world.item.crafting.Recipe.class) public interface RecipeMixin_API extends Recipe { // @formatter:off - @Shadow ItemStack shadow$assemble(I inv, HolderLookup.Provider registryAccess); - @Shadow net.minecraft.world.item.ItemStack shadow$getResultItem(HolderLookup.Provider registryAccess); + @Shadow ItemStack shadow$assemble(I inv); @Shadow boolean shadow$isSpecial(); @Shadow boolean shadow$matches(I inv, net.minecraft.world.level.Level worldIn); - @Shadow NonNullList shadow$getRemainingItems(I inv); @Shadow net.minecraft.world.item.crafting.RecipeType shadow$getType(); - @Shadow NonNullList shadow$getIngredients(); - // @formatter:on + @Shadow List shadow$display(); +// @formatter:on @NonNull @Override default ItemStackSnapshot exemplaryResult() { - return ItemStackUtil.snapshotOf(this.shadow$getResultItem(SpongeCommon.server().registryAccess())); + return this.shadow$display().stream() + .map(RecipeDisplay::result) + .map(sd -> sd.resolveForFirstStack(RecipeUtil.serverBasedContextMap())) + .filter(Predicate.not(ItemStack::isEmpty)) + .map(ItemStackUtil::snapshotOf) + .findFirst() + .orElseGet(ItemStackSnapshot::empty); } @Override default boolean isValid(@NonNull final I2 inv, @NonNull final ServerWorld world) { - return this.shadow$matches((I) toCraftingInputOrThrow(inv), (net.minecraft.world.level.Level) world); + return this.shadow$matches((I) InventoryUtil.toCraftingInputOrThrow(inv), (net.minecraft.world.level.Level) world); } @NonNull @Override default ItemStackSnapshot result(@NonNull final I2 inv) { - return ItemStackUtil.snapshotOf(this.shadow$assemble((I) toCraftingInputOrThrow(inv), SpongeCommon.server().registryAccess())); + return ItemStackUtil.snapshotOf(this.shadow$assemble((I) InventoryUtil.toCraftingInputOrThrow(inv))); } @NonNull @Override default List remainingItems(@NonNull final I2 inv) { - final var nonNullList = this.shadow$getRemainingItems((I) toCraftingInputOrThrow(inv)); - return nonNullList.stream() - .map(ItemStackUtil::snapshotOf) - .collect(Collectors.toList()); - } - - @Override - default List ingredients() { - return this.shadow$getIngredients().stream().map(IngredientUtil::fromNative).collect(Collectors.toList()); + return this.shadow$display().stream() + .map(RecipeDisplay::result) + .filter(sd -> sd instanceof SlotDisplay.WithRemainder) + .map(SlotDisplay.WithRemainder.class::cast) + .map(SlotDisplay.WithRemainder::remainder) + .map(sd -> sd.resolveForFirstStack(RecipeUtil.serverBasedContextMap())) + .filter(Predicate.not(ItemStack::isEmpty)) + .map(ItemStackUtil::snapshotOf) + .toList(); } @Override @@ -98,4 +103,9 @@ default boolean isDynamic() { default RecipeType type() { return (RecipeType) this.shadow$getType(); } + + @Override + default Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, net.minecraft.world.item.crafting.Recipe.CODEC, (net.minecraft.world.item.crafting.Recipe) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/ShapedRecipeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/ShapedRecipeMixin_API.java index 595db6d9223..e6d14bab52c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/ShapedRecipeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/ShapedRecipeMixin_API.java @@ -26,10 +26,16 @@ import net.minecraft.world.item.crafting.ShapedRecipe; import net.minecraft.world.item.crafting.ShapedRecipePattern; +import org.spongepowered.api.item.recipe.crafting.Ingredient; import org.spongepowered.api.item.recipe.crafting.ShapedCraftingRecipe; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.item.recipe.ingredient.IngredientUtil; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; @Mixin(ShapedRecipe.class) public abstract class ShapedRecipeMixin_API implements ShapedCraftingRecipe { @@ -52,6 +58,12 @@ public org.spongepowered.api.item.recipe.crafting.Ingredient ingredient(final in return ((org.spongepowered.api.item.recipe.crafting.Ingredient)(Object) this.pattern.ingredients().get(recipeItemIndex)); } + @Override + public List ingredients() { + return this.pattern.ingredients().stream().filter(Optional::isPresent).map(Optional::get) + .map(IngredientUtil::fromNative).collect(Collectors.toList()); + } + @Override public int width() { return this.pattern.width(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/ShapelessRecipeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/ShapelessRecipeMixin_API.java index 94a417304e9..6414b4aa581 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/ShapelessRecipeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/ShapelessRecipeMixin_API.java @@ -25,10 +25,27 @@ package org.spongepowered.common.mixin.api.minecraft.world.item.crafting; import net.minecraft.world.item.crafting.ShapelessRecipe; +import org.spongepowered.api.item.recipe.crafting.Ingredient; import org.spongepowered.api.item.recipe.crafting.ShapelessCraftingRecipe; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.item.recipe.ingredient.IngredientUtil; + +import java.util.List; +import java.util.stream.Collectors; @Mixin(ShapelessRecipe.class) public abstract class ShapelessRecipeMixin_API implements ShapelessCraftingRecipe { + // @formatter:off + @Shadow @Final private List ingredients; + // @formatter:on + + @Override + public List ingredients() { + return this.ingredients.stream().map(IngredientUtil::fromNative).collect(Collectors.toList()); + } + + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/SingleItemRecipeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/SingleItemRecipeMixin_API.java index 72b94a6aa03..d7233ca9379 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/SingleItemRecipeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/SingleItemRecipeMixin_API.java @@ -24,10 +24,16 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.item.crafting; +import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.SingleItemRecipe; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; @Mixin(SingleItemRecipe.class) public abstract class SingleItemRecipeMixin_API { + // @formatter:off + @Shadow public abstract Ingredient shadow$input(); + // @formatter:on + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/SmithingTransformRecipeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/SmithingTransformRecipeMixin_API.java index 44c31a04ae4..24b553d5fb6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/SmithingTransformRecipeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/SmithingTransformRecipeMixin_API.java @@ -25,11 +25,36 @@ package org.spongepowered.common.mixin.api.minecraft.world.item.crafting; import net.minecraft.world.item.crafting.SmithingTransformRecipe; +import org.spongepowered.api.item.recipe.crafting.Ingredient; import org.spongepowered.api.item.recipe.smithing.SmithingRecipe; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Optional; @Mixin(SmithingTransformRecipe.class) public abstract class SmithingTransformRecipeMixin_API implements SmithingRecipe { + // @formatter:off + @Shadow @Final Optional addition; + @Shadow @Final Optional template; + @Shadow @Final net.minecraft.world.item.crafting.Ingredient base; + // @formatter:on + + @Override + public Optional templateIngredient() { + return this.template.map(Ingredient.class::cast); + } + + @Override + public Optional baseIngredient() { + return Optional.of((Ingredient) (Object) this.base); + } + + @Override + public Optional additionalIngredient() { + return this.addition.map(Ingredient.class::cast); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/StonecutterRecipeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/StonecutterRecipeMixin_API.java index 5af37c839a3..63a38f12cb6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/StonecutterRecipeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/crafting/StonecutterRecipeMixin_API.java @@ -27,8 +27,15 @@ import net.minecraft.world.item.crafting.StonecutterRecipe; import org.spongepowered.api.item.recipe.single.StoneCutterRecipe; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.item.recipe.ingredient.IngredientUtil; @Mixin(StonecutterRecipe.class) public abstract class StonecutterRecipeMixin_API extends SingleItemRecipeMixin_API implements StoneCutterRecipe { + @Override + public org.spongepowered.api.item.recipe.crafting.Ingredient ingredient() { + return IngredientUtil.fromNative(this.shadow$input()); + } + + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/armortrim/ArmorTrimMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/equipment/trim/ArmorTrimMixin_API.java similarity index 86% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/armortrim/ArmorTrimMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/equipment/trim/ArmorTrimMixin_API.java index c6e8f66093f..8c89e6284e2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/armortrim/ArmorTrimMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/equipment/trim/ArmorTrimMixin_API.java @@ -22,21 +22,20 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.item.armortrim; +package org.spongepowered.common.mixin.api.minecraft.world.item.equipment.trim; import net.minecraft.core.Holder; -import net.minecraft.world.item.armortrim.ArmorTrim; import org.spongepowered.api.item.recipe.smithing.TrimMaterial; import org.spongepowered.api.item.recipe.smithing.TrimPattern; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -@Mixin(ArmorTrim.class) +@Mixin(net.minecraft.world.item.equipment.trim.ArmorTrim.class) public abstract class ArmorTrimMixin_API implements org.spongepowered.api.item.recipe.smithing.ArmorTrim { // @formatter:off - @Shadow public abstract Holder shadow$material(); - @Shadow public abstract Holder shadow$pattern(); + @Shadow public abstract Holder shadow$material(); + @Shadow public abstract Holder shadow$pattern(); // @formatter:on @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/armortrim/TrimMaterialMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/equipment/trim/TrimMaterialMixin_API.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/armortrim/TrimMaterialMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/equipment/trim/TrimMaterialMixin_API.java index daef56e601e..80c5699dacd 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/armortrim/TrimMaterialMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/equipment/trim/TrimMaterialMixin_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.item.armortrim; +package org.spongepowered.common.mixin.api.minecraft.world.item.equipment.trim; -import net.minecraft.world.item.armortrim.TrimMaterial; +import net.minecraft.world.item.equipment.trim.TrimMaterial; import org.spongepowered.asm.mixin.Mixin; @Mixin(TrimMaterial.class) diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/armortrim/TrimPatternMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/equipment/trim/TrimPatternMixin_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/armortrim/TrimPatternMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/equipment/trim/TrimPatternMixin_API.java index c50fa5f711b..54d640c44d0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/armortrim/TrimPatternMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/equipment/trim/TrimPatternMixin_API.java @@ -22,11 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.item.armortrim; +package org.spongepowered.common.mixin.api.minecraft.world.item.equipment.trim; -import net.minecraft.world.item.armortrim.TrimPattern; import org.spongepowered.asm.mixin.Mixin; -@Mixin(TrimPattern.class) +@Mixin(net.minecraft.world.item.equipment.trim.TrimPattern.class) public class TrimPatternMixin_API implements org.spongepowered.api.item.recipe.smithing.TrimPattern { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/trading/VillagerTradeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/trading/VillagerTradeMixin_API.java new file mode 100644 index 00000000000..d4a0f7fd82d --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/trading/VillagerTradeMixin_API.java @@ -0,0 +1,80 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.item.trading; + +import net.minecraft.core.registries.Registries; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.npc.villager.VillagerProfession; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.TradeSet; +import net.minecraft.world.item.trading.VillagerTrade; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.LootParams; +import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.type.ProfessionTypes; +import org.spongepowered.api.item.merchant.TradeOffer; +import org.spongepowered.api.item.merchant.TradeOfferGenerator; +import org.spongepowered.api.util.RandomProvider; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.util.VecHelper; + +import java.util.Optional; + +@Mixin(VillagerTrade.class) +public abstract class VillagerTradeMixin_API implements TradeOfferGenerator { + + // @formatter:off + @Shadow public abstract MerchantOffer shadow$getOffer(final LootContext lootContext); + // @formatter:on + + @Override + public TradeOffer apply(final org.spongepowered.api.entity.Entity merchant, final RandomProvider.Source random) { + if (!(merchant.world() instanceof ServerLevel sl)) { + throw new IllegalStateException("The merchant is not in a server world!"); + } + final var profession = merchant.get(Keys.PROFESSION_TYPE).orElseGet(ProfessionTypes.NITWIT); + final var level = merchant.get(Keys.PROFESSION_LEVEL).orElse(1); + final var trades = ((VillagerProfession) (Object) profession).getTrades(level); + + Optional tradeSetOpt = sl.registryAccess().lookupOrThrow(Registries.TRADE_SET).getOptional(trades); + if (tradeSetOpt.isEmpty()) { + throw new IllegalStateException("The merchant is not in a server world!"); + } + + LootContext lootContext = new LootContext.Builder( + new LootParams.Builder(sl) + .withParameter(LootContextParams.ORIGIN, VecHelper.toVanillaVector3d(merchant.position())) + .withParameter(LootContextParams.THIS_ENTITY, (Entity) merchant) + .create(LootContextParamSets.VILLAGER_TRADE) + ) + .create(tradeSetOpt.get().randomSequence()); + return (TradeOffer) this.shadow$getOffer(lootContext); + } + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/BlockGetterMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/BlockGetterMixin_API.java index c0aa643a6db..c24ffd822ba 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/BlockGetterMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/BlockGetterMixin_API.java @@ -51,14 +51,8 @@ public interface BlockGetterMixin_API extends PrimitiveGameVolume { @Shadow BlockState shadow$getBlockState(BlockPos p_180495_1_); @Shadow net.minecraft.world.level.material.FluidState shadow$getFluidState(BlockPos p_204610_1_); @Shadow int shadow$getLightEmission(BlockPos p_217298_1_); - @Shadow int shadow$getMaxLightLevel(); //@formatter:on - @Override - default int maximumLight() { - return this.shadow$getMaxLightLevel(); - } - @Override default int emittedLight(final Vector3i position) { return this.shadow$getLightEmission(new BlockPos(position.x(), position.y(), position.z())); @@ -71,7 +65,7 @@ default int emittedLight(final int x, final int y, final int z) { @Override default int height() { - return ((LevelHeightAccessor) this).getMaxBuildHeight(); + return ((LevelHeightAccessor) this).getHeight(); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/GameRules_KeyMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/GameRules_KeyMixin_API.java index 39b2a935325..df194a26208 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/GameRules_KeyMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/GameRules_KeyMixin_API.java @@ -24,49 +24,42 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level; -import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.gamerules.GameRuleType; import org.spongepowered.api.world.gamerule.GameRule; import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.accessor.world.level.GameRulesAccessor; import java.lang.reflect.Type; -@Mixin(GameRules.Key.class) -public class GameRules_KeyMixin_API implements GameRule { +@Mixin(net.minecraft.world.level.gamerules.GameRule.class) +@Implements(@Interface(iface = GameRule.class, prefix = "api$")) +public abstract class GameRules_KeyMixin_API implements GameRule { - @Shadow @Final private String id; + @Shadow public abstract String shadow$id(); + + @Shadow private @Final GameRuleType gameRuleType; + @Shadow @Final private T defaultValue; @Override public String name() { - return this.id; + return this.shadow$id(); } @Override public Type valueType() { - final GameRules.Type type = GameRulesAccessor.accessor$GAME_RULE_TYPES().get(this); - final GameRules.Value value = type.createRule(); - if (value instanceof GameRules.BooleanValue) { - return Boolean.class; - } - if (value instanceof GameRules.IntegerValue) { - return Integer.class; - } - throw new IllegalStateException("Unexpected GameRule.Value implementation " + value.getClass().getName()); + return switch (this.gameRuleType) { + case INT -> Integer.class; + case BOOL -> Boolean.class; + default -> throw new IllegalStateException("Unexpected GameRuleType " + this.gameRuleType); + }; } - @Override - @SuppressWarnings("unchecked") - public T defaultValue() { - final GameRules.Type type = GameRulesAccessor.accessor$GAME_RULE_TYPES().get(this); - final GameRules.Value value = type.createRule(); - if (value instanceof GameRules.BooleanValue) { - return (T) (Object)((GameRules.BooleanValue) value).get(); - } - if (value instanceof GameRules.IntegerValue) { - return (T)(Object)((GameRules.IntegerValue) value).get(); - } - throw new IllegalStateException("Unexpected GameRule.Value implementation " + value.getClass().getName()); + @Intrinsic + public T api$defaultValue() { + return this.defaultValue; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelAccessorMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelAccessorMixin_API.java index 1becec8565e..b6386121431 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelAccessorMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelAccessorMixin_API.java @@ -82,7 +82,7 @@ default Palette blockPalette( return PaletteWrapper.of( PaletteTypes.BLOCK_STATE_PALETTE.get(), Block.BLOCK_STATE_REGISTRY, - (org.spongepowered.api.registry.Registry) ((LevelAccessor) (Object) this).registryAccess().registryOrThrow(Registries.BLOCK) + (org.spongepowered.api.registry.Registry) ((LevelAccessor) (Object) this).registryAccess().lookupOrThrow(Registries.BLOCK) ); } @@ -98,8 +98,8 @@ default boolean setBiome(final int x, final int y, final int z, final org.sponge return false; } - return VolumeStreamUtils.setBiomeOnNativeChunk(x, y, z, ((LevelReader) this).getMinBuildHeight(), ((LevelReader) this).getHeight(), - biome, i -> iChunk.getSection(iChunk.getSectionIndex(i)), () -> iChunk.setUnsaved(true)); + return VolumeStreamUtils.setBiomeOnNativeChunk(x, y, z, ((LevelReader) this).getMinY(), ((LevelReader) this).getHeight(), + biome, i -> iChunk.getSection(iChunk.getSectionIndex(i)), iChunk::markUnsaved); } // Volume @@ -172,7 +172,7 @@ default boolean setBlock(final int x, final int y, final int z, final org.sponge if (!((Level) this).isInWorldBounds(new BlockPos(x, y, z))) { throw new PositionOutOfBoundsException(new Vector3i(x, y, z), Constants.World.BLOCK_MIN, Constants.World.BLOCK_MAX); } - try (final @Nullable PhaseContext<@NonNull ?> context = PluginPhase.State.BLOCK_WORKER.switchIfNecessary(PhaseTracker.SERVER)) { + try (final @Nullable PhaseContext<@NonNull ?> context = PluginPhase.State.BLOCK_WORKER.switchIfNecessary(PhaseTracker.getWorldInstance((LevelAccessor) this))) { if (context != null) { context.buildAndSwitch(); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelMixin_API.java index 801a321a086..336b7267fab 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelMixin_API.java @@ -24,14 +24,15 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level; +import com.google.common.collect.Iterables; import net.kyori.adventure.sound.Sound; import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; +import net.minecraft.core.RegistryAccess; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerChunkCache; -import net.minecraft.util.Tuple; -import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.level.BlockAndLightGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelReader; @@ -43,6 +44,8 @@ import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.TagValueOutput; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.block.entity.BlockEntity; @@ -53,6 +56,7 @@ import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.service.context.Context; import org.spongepowered.api.util.AABB; +import org.spongepowered.api.util.Tuple; import org.spongepowered.api.world.HeightTypes; import org.spongepowered.api.world.LightType; import org.spongepowered.api.world.Location; @@ -119,10 +123,12 @@ public abstract class LevelMixin_API, L extends Location entityTypeTest, net.minecraft.world.phys.AABB param1, @Nullable Predicate param2); + @Shadow public abstract RegistryAccess shadow$registryAccess(); // @formatter:on + private Context api$context; private RegistryHolderLogic api$registryHolder; protected @MonotonicNonNull SpongeChunkLayout api$chunkLayout; @@ -176,7 +182,8 @@ public Iterable loadedChunks() { if (chunkProvider instanceof ServerChunkCache) { final ChunkMapAccessor chunkManager = (ChunkMapAccessor) ((ServerChunkCache) chunkProvider).chunkMap; final List chunks = new ArrayList<>(); - chunkManager.invoker$getChunks().forEach(holder -> { + final var visibleChunks = chunkManager.accessor$visibleChunkMap(); + Iterables.unmodifiableIterable(visibleChunks.values()).forEach(holder -> { final WorldChunk chunk = (WorldChunk) holder.getTickingChunk(); if (chunk != null) { chunks.add(chunk); @@ -222,7 +229,7 @@ public Vector3i size() { private SpongeChunkLayout api$chunkLayout() { if (this.api$chunkLayout == null) { - final var min = ((Level) (Object) this).getMinBuildHeight(); + final var min = ((Level) (Object) this).getMinY(); final var height = ((Level) (Object) this).getHeight(); this.api$chunkLayout = new SpongeChunkLayout(min, height); } @@ -234,7 +241,7 @@ public Vector3i size() { @Override public Context context() { if (this.api$context == null) { - this.api$context = new Context(Context.WORLD_KEY, this.shadow$dimension().location().toString()); + this.api$context = new Context(Context.WORLD_KEY, this.shadow$dimension().identifier().toString()); } return this.api$context; } @@ -282,8 +289,9 @@ public void addBlockEntity(final int x, final int y, final int z, final BlockEnt // BlockEntity stores its location, as well as it being mutable and stuff, so just setting what we've given here // would cause unexpected bugs. final net.minecraft.world.level.block.entity.BlockEntity mcOriginalBlockEntity = (net.minecraft.world.level.block.entity.BlockEntity) Objects.requireNonNull(blockEntity, "blockEntity"); + final var output = TagValueOutput.createWithContext(ProblemReporter.DISCARDING, this.shadow$registryAccess()); // Save the nbt so we can copy it, specifically wout the metadata of x,y,z coordinates - final CompoundTag tag = mcOriginalBlockEntity.saveWithId(mcOriginalBlockEntity.getLevel().registryAccess()); + mcOriginalBlockEntity.saveWithId(output); // Ensure that where we are placing this blockentity is the right blockstate, so that minecraft will actually accept it. this.world().setBlock(x, y, z, (org.spongepowered.api.block.BlockState) mcOriginalBlockEntity.getBlockState()); @@ -292,7 +300,8 @@ public void addBlockEntity(final int x, final int y, final int z, final BlockEnt .orElseThrow(() -> new IllegalStateException("Failed to create Block Entity at " + this.location(Vector3i.from(x, y, z)))); // Load the data into it. - mcNewBlockEntity.loadWithComponents(tag, mcOriginalBlockEntity.getLevel().registryAccess()); + final var input = TagValueInput.create(ProblemReporter.DISCARDING, this.shadow$registryAccess(), output.buildResult()); + mcNewBlockEntity.loadWithComponents(input); // Finally, inform minecraft about our actions. this.shadow$setBlockEntity(mcNewBlockEntity); } @@ -464,7 +473,7 @@ public Weather weather() { @Override public int light(final LightType type, final int x, final int y, final int z) { - var thisLevel = ((BlockAndTintGetter) this); + var thisLevel = ((BlockAndLightGetter) this); return thisLevel.getBrightness((LightLayer) (Object) type, new BlockPos(x, y, z)); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelReaderMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelReaderMixin_API.java index 236f5c76662..d56b8ba6e71 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelReaderMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/LevelReaderMixin_API.java @@ -28,7 +28,7 @@ import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.Registries; -import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.BlockAndLightGetter; import net.minecraft.world.level.CollisionGetter; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Block; @@ -89,7 +89,7 @@ default Palette blockPalette() { return PaletteWrapper.of( PaletteTypes.BLOCK_STATE_PALETTE.get(), Block.BLOCK_STATE_REGISTRY, - (Registry) this.shadow$registryAccess().registryOrThrow(Registries.BLOCK) + (Registry) this.shadow$registryAccess().lookupOrThrow(Registries.BLOCK) ); } @@ -110,7 +110,7 @@ default WorldBorder border() { default WorldBorder setBorder(final WorldBorder border) { final WorldBorder worldBorder = ((WorldBorderBridge) ((CollisionGetter) this).getWorldBorder()).bridge$applyFrom(border); if (worldBorder == null) { - return (WorldBorder) net.minecraft.world.level.border.WorldBorder.DEFAULT_SETTINGS; + return (WorldBorder) (Object) net.minecraft.world.level.border.WorldBorder.Settings.DEFAULT; } return worldBorder; } @@ -122,7 +122,7 @@ default boolean isInBorder(final Entity entity) { @Override default boolean canSeeSky(final int x, final int y, final int z) { - return ((BlockAndTintGetter) this).canSeeSky(new BlockPos(x, y, z)); + return ((BlockAndLightGetter) this).canSeeSky(new BlockPos(x, y, z)); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/ExplosionMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/ServerExplosionMixin_API.java similarity index 86% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/ExplosionMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/ServerExplosionMixin_API.java index 480737ccfea..ddf60b67eff 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/ExplosionMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/ServerExplosionMixin_API.java @@ -24,7 +24,9 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.entity.explosive.Explosive; import org.spongepowered.api.world.explosion.Explosion; @@ -34,21 +36,22 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.bridge.world.level.ExplosionBridge; +import org.spongepowered.common.util.VecHelper; import java.util.Optional; -@Mixin(net.minecraft.world.level.Explosion.class) -public abstract class ExplosionMixin_API implements Explosion { +@Mixin(net.minecraft.world.level.ServerExplosion.class) +public abstract class ServerExplosionMixin_API implements Explosion { //@formatter:off @Shadow @Final private boolean fire; - @Shadow @Final private net.minecraft.world.level.Level level; - @Shadow @Final private double x; - @Shadow @Final private double y; - @Shadow @Final private double z; + @Shadow @Final private ServerLevel level; + @Shadow @Final private Entity source; @Shadow @Final private float radius; @Shadow @Final private net.minecraft.world.level.Explosion.BlockInteraction blockInteraction; + @Shadow @Final private Vec3 center; + //@formatter:on @Nullable private ServerLocation api$location; @@ -56,7 +59,7 @@ public abstract class ExplosionMixin_API implements Explosion { @Override public ServerLocation location() { if (this.api$location == null) { - this.api$location = ServerLocation.of((ServerWorld) this.level, this.x, this.y, this.z); + this.api$location = ServerLocation.of((ServerWorld) this.level, VecHelper.toVector3d(this.center)); } return this.api$location; } @@ -87,7 +90,10 @@ public boolean shouldPlaySmoke() { @Override public boolean shouldBreakBlocks() { - return ((ExplosionBridge) this).bridge$getShouldDamageBlocks(); + return switch (this.blockInteraction) { + case KEEP, TRIGGER_BLOCK -> false; + case DESTROY, DESTROY_WITH_DECAY -> true; + }; } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/BiomeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/BiomeMixin_API.java index dc0b59e52f7..93065d9feff 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/BiomeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/biome/BiomeMixin_API.java @@ -24,37 +24,20 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level.biome; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.tags.TagKey; -import org.spongepowered.api.registry.DefaultedRegistryType; -import org.spongepowered.api.registry.RegistryTypes; -import org.spongepowered.api.tag.Tag; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.world.biome.Biome; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.data.holder.SpongeDataHolder; +import org.spongepowered.common.util.DataPackUtil; -import java.util.Collection; +import java.util.Optional; @Mixin(net.minecraft.world.level.biome.Biome.class) public abstract class BiomeMixin_API implements Biome, SpongeDataHolder { @Override - public DefaultedRegistryType registryType() { - return RegistryTypes.BIOME; - } - - @Override - public Collection> tags() { - return this.registryType().get().tags().filter(this::is).toList(); - } - - @Override - public boolean is(final Tag tag) { - final Registry registry = SpongeCommon.vanillaRegistry(Registries.BIOME); - final Holder.Reference holder = registry.createIntrusiveHolder((net.minecraft.world.level.biome.Biome) (Object) this); - return holder.is(((TagKey) (Object) tag)); + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, net.minecraft.world.level.biome.Biome.DIRECT_CODEC, (net.minecraft.world.level.biome.Biome) (Object) this); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/CopperGolemStatueBlock_PoseMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/CopperGolemStatueBlock_PoseMixin_API.java new file mode 100644 index 00000000000..0ced2233a87 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/CopperGolemStatueBlock_PoseMixin_API.java @@ -0,0 +1,33 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.level.block; + +import net.minecraft.world.level.block.CopperGolemStatueBlock; +import org.spongepowered.api.data.type.CopperGolemPose; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(CopperGolemStatueBlock.Pose.class) +public abstract class CopperGolemStatueBlock_PoseMixin_API implements CopperGolemPose { +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/WeatheringCopper_WeatherStateMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/WeatheringCopper_WeatherStateMixin_API.java new file mode 100644 index 00000000000..a01410ae563 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/WeatheringCopper_WeatherStateMixin_API.java @@ -0,0 +1,33 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.level.block; + +import net.minecraft.world.level.block.WeatheringCopper; +import org.spongepowered.api.data.type.CopperOxidation; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(WeatheringCopper.WeatherState.class) +public abstract class WeatheringCopper_WeatherStateMixin_API implements CopperOxidation { +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/AbstractFurnaceBlockEntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/AbstractFurnaceBlockEntityMixin_API.java index d2f65b5e945..66250438b89 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/AbstractFurnaceBlockEntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/AbstractFurnaceBlockEntityMixin_API.java @@ -42,10 +42,10 @@ public abstract class AbstractFurnaceBlockEntityMixin_API extends BaseContainerBlockEntityMixin_API implements FurnaceBlockEntity { // @formatter:off - @Shadow int litTime; - @Shadow int litDuration; - @Shadow int cookingProgress; - @Shadow int cookingTotalTime; + @Shadow private int litTimeRemaining; + @Shadow private int litTotalTime; + @Shadow private int cookingTimer; + @Shadow private int cookingTotalTime; @Shadow protected NonNullList items; // @formatter:on @@ -64,9 +64,9 @@ public abstract class AbstractFurnaceBlockEntityMixin_API extends BaseContainerB @Override public DataContainer toContainer() { return super.toContainer() - .set(Constants.TileEntity.Furnace.BURN_TIME, this.litTime) - .set(Constants.TileEntity.Furnace.BURN_TIME_TOTAL, this.litDuration) - .set(Constants.TileEntity.Furnace.COOK_TIME, this.cookingProgress) + .set(Constants.TileEntity.Furnace.BURN_TIME, this.litTimeRemaining) + .set(Constants.TileEntity.Furnace.BURN_TIME_TOTAL, this.litTotalTime) + .set(Constants.TileEntity.Furnace.COOK_TIME, this.cookingTimer) .set(Constants.TileEntity.Furnace.COOK_TIME_TOTAL, this.cookingTotalTime); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BaseContainerBlockEntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BaseContainerBlockEntityMixin_API.java index 02911fdedda..a5a0fdfbcdb 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BaseContainerBlockEntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BaseContainerBlockEntityMixin_API.java @@ -54,9 +54,10 @@ public abstract class BaseContainerBlockEntityMixin_API extends BlockEntityMixin @Override public DataContainer toContainer() { final DataContainer container = super.toContainer(); - if (this.lockKey != null) { - container.set(Constants.TileEntity.LOCK_CODE, this.lockKey.key()); - } + // TODO - figure out lock code serialization +// if (this.lockKey != null) { +// container.set(Constants.TileEntity.LOCK_CODE, this.lockKey.key()); +// } final List items = Lists.newArrayList(); for (int i = 0; i < ((Container) this).getContainerSize(); i++) { final ItemStack stack = ((Container) this).getItem(i); diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BeaconBlockEntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BeaconBlockEntityMixin_API.java index a4eee2d4265..2ed2b94ae21 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BeaconBlockEntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BeaconBlockEntityMixin_API.java @@ -41,9 +41,9 @@ public abstract class BeaconBlockEntityMixin_API extends BlockEntityMixin_API implements Beacon { // @formatter:off - @Shadow int levels; - @Shadow @Nullable Holder primaryPower; - @Shadow @Nullable Holder secondaryPower; + @Shadow private int levels; + @Shadow private @Nullable Holder primaryPower; + @Shadow private @Nullable Holder secondaryPower; // @formatter:on @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BlockEntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BlockEntityMixin_API.java index 34fcbb17ae5..75e3f3936b9 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BlockEntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BlockEntityMixin_API.java @@ -63,6 +63,7 @@ public abstract class BlockEntityMixin_API implements BlockEntity { @Shadow @Final private net.minecraft.world.level.block.entity.BlockEntityType type; @Shadow protected net.minecraft.world.level.Level level; @Shadow protected boolean remove; + @Shadow private net.minecraft.world.level.block.state.BlockState blockState; @Shadow public abstract BlockPos shadow$getBlockPos(); @Shadow public abstract CompoundTag shadow$saveWithFullMetadata(HolderLookup.Provider $$0); @Shadow @Final protected BlockPos worldPosition; @@ -70,6 +71,9 @@ public abstract class BlockEntityMixin_API implements BlockEntity { @Shadow public abstract void shadow$setChanged(); //@formatter:on + @Shadow + public abstract CompoundTag saveWithFullMetadata(HolderLookup.Provider $$0); + @Nullable private LocatableBlock api$LocatableBlock; private boolean api$canTickRequested = false; private boolean api$canTick; @@ -85,7 +89,7 @@ public ServerLocation serverLocation() { throw new RuntimeException("The TileEntity has not been spawned in a world yet!"); } - if (this.level.isClientSide) { + if (this.level.isClientSide()) { throw new RuntimeException("You should not attempt to make a server-side location on the client!"); } @@ -179,7 +183,7 @@ public final BlockEntityType type() { @Override public BlockState block() { - return (BlockState) this.level.getBlockState(this.shadow$getBlockPos()); + return (BlockState) this.blockState; } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/CommandBlockEntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/CommandBlockEntityMixin_API.java index 119aece2369..848ed9d0a87 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/CommandBlockEntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/CommandBlockEntityMixin_API.java @@ -28,6 +28,7 @@ import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.BaseCommandBlock; import net.minecraft.world.level.block.entity.CommandBlockEntity; import org.checkerframework.checker.nullness.qual.NonNull; @@ -36,6 +37,7 @@ import org.spongepowered.api.data.value.Value; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.accessor.world.level.BaseCommandBlockAccessor; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.util.Constants; @@ -50,7 +52,10 @@ public abstract class CommandBlockEntityMixin_API extends BlockEntityMixin_API i @Override public void execute() { - this.shadow$getCommandBlock().performCommand(this.level); + if (!(this.level instanceof ServerLevel sl)) { + return; + } + this.shadow$getCommandBlock().performCommand(sl); } @Override @@ -64,8 +69,8 @@ public DataContainer toContainer() { container.set(Constants.TileEntity.CommandBlock.STORED_COMMAND, this.shadow$getCommandBlock().getCommand()); container.set(Constants.TileEntity.CommandBlock.SUCCESS_COUNT, this.shadow$getCommandBlock().getSuccessCount()); container.set(Constants.TileEntity.CUSTOM_NAME, this.shadow$getCommandBlock().getName()); - container.set(Constants.TileEntity.CommandBlock.DOES_TRACK_OUTPUT, this.shadow$getCommandBlock().shouldInformAdmins()); - if (this.shadow$getCommandBlock().shouldInformAdmins()) { + container.set(Constants.TileEntity.CommandBlock.DOES_TRACK_OUTPUT, this.shadow$getCommandBlock().isTrackOutput()); + if (this.shadow$getCommandBlock().isTrackOutput()) { container.set(Constants.TileEntity.CommandBlock.TRACKED_OUTPUT, LegacyComponentSerializer.legacySection().serialize(SpongeAdventure.asAdventure(this.shadow$getCommandBlock().getLastOutput()))); } return container; @@ -89,8 +94,17 @@ public String identifier() { return this.shadow$getCommandBlock().getName().getString(); } + @SuppressWarnings({"UnstableApiUsage", "deprecation"}) @Override public void sendMessage(final @NonNull Identity identity, final @NonNull Component message, final @NonNull MessageType type) { - this.shadow$getCommandBlock().sendSystemMessage(SpongeAdventure.asVanilla(message)); + final var block = this.shadow$getCommandBlock(); + if (!(this.level instanceof ServerLevel sl)) { + return; + } + final var source = ((BaseCommandBlockAccessor) block).invoker$createSource(sl); + if (source == null) { + return; + } + source.sendSystemMessage(SpongeAdventure.asVanilla(message)); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BedBlockEntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/PotentSulfurBlockEntityMixin_API.java similarity index 76% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BedBlockEntityMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/PotentSulfurBlockEntityMixin_API.java index fe3bbde4d14..8a23483cf04 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/BedBlockEntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/PotentSulfurBlockEntityMixin_API.java @@ -24,23 +24,24 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level.block.entity; -import net.minecraft.world.level.block.entity.BedBlockEntity; -import org.spongepowered.api.block.entity.Bed; +import net.minecraft.world.level.block.entity.PotentSulfurBlockEntity; +import org.spongepowered.api.block.entity.PotentSulfur; +import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.value.Value; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import java.util.Set; -@Mixin(BedBlockEntity.class) -public abstract class BedBlockEntityMixin_API extends BlockEntityMixin_API implements Bed { +@Mixin(PotentSulfurBlockEntity.class) +public abstract class PotentSulfurBlockEntityMixin_API extends BlockEntityMixin_API implements PotentSulfur { + + @Shadow public abstract void resetCountdown(); @Override protected Set> api$getVanillaValues() { final Set> values = super.api$getVanillaValues(); - - values.add(this.color().asImmutable()); - + this.getValue(Keys.ERUPTION_COUNTDOWN).map(Value::asImmutable).ifPresent(values::add); return values; } - } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/SpawnerBlockEntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/SpawnerBlockEntityMixin_API.java index 18c8c25b2c6..ddf98728581 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/SpawnerBlockEntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/SpawnerBlockEntityMixin_API.java @@ -51,7 +51,7 @@ public void spawnEntityBatchImmediately(final boolean force) { bridge.bridge$setMaxNearbyEntities(Short.MAX_VALUE); bridge.bridge$setSpawnDelay(0); - if (!this.level.isClientSide) { + if (!this.level.isClientSide()) { this.shadow$getSpawner().serverTick((ServerLevel) this.level, this.worldPosition); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/TrialSpawnerBlockEntityMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/TrialSpawnerBlockEntityMixin_API.java index 28f49bdd338..83e43cc55f2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/TrialSpawnerBlockEntityMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/entity/TrialSpawnerBlockEntityMixin_API.java @@ -40,25 +40,25 @@ public abstract class TrialSpawnerBlockEntityMixin_API extends BlockEntityMixin_ @Override public void spawnImmediately(final boolean force) { - if (this.level.isClientSide) { + if (this.level.isClientSide()) { return; } final var thisLevel = (ServerLevel) this.level; final var spawner = this.getTrialSpawner(); - final var spawnerData = (TrialSpawnerDataAccessor) spawner.getData(); - final var spawnerConfig = spawner.getConfig(); + final var spawnerData = (TrialSpawnerDataAccessor) spawner.getStateData(); + final var spawnerConfig = spawner.activeConfig(); - final var additionalPlayers = spawner.getData().countAdditionalPlayers(this.worldPosition); + final var additionalPlayers = spawner.getStateData().countAdditionalPlayers(this.worldPosition); final var nextSpawnAt = spawnerData.accessor$nextMobSpawnsAt(); spawnerData.accessor$nextMobSpawnsAt(0); - if (force || spawner.getData().isReadyToSpawnNextMob(thisLevel, spawnerConfig, additionalPlayers)) { + if (force || spawner.getStateData().isReadyToSpawnNextMob(thisLevel, spawnerConfig, additionalPlayers)) { // See TrialSpawnerState#tickAndGetNext spawner.spawnMob(thisLevel, this.worldPosition).ifPresent(spawned -> { spawnerData.accessor$currentMobs().add(spawned); spawnerData.accessor$totalMobsSpawned(spawnerData.accessor$totalMobsSpawned() + 1); spawnerData.accessor$nextMobSpawnsAt(this.level.getGameTime() + (long) spawnerConfig.ticksBetweenSpawn()); spawnerConfig.spawnPotentialsDefinition().getRandom(this.level.getRandom()).ifPresent(next -> { - spawnerData.accessor$nextSpawnData(Optional.of(next.data())); + spawnerData.accessor$nextSpawnData(Optional.of(next)); spawner.markUpdated(); }); }); diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/StateHolderMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/StateHolderMixin_API.java index 7ec60477d4b..5192ff21652 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/StateHolderMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/StateHolderMixin_API.java @@ -45,9 +45,9 @@ import org.spongepowered.common.util.PortionTypeUtil; import java.util.Collection; -import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import java.util.stream.Stream; @Mixin(StateHolder.class) public abstract class StateHolderMixin_API, C> implements State, SpongeImmutableDataHolder { @@ -57,7 +57,7 @@ public abstract class StateHolderMixin_API, C> implements Sta @Shadow public abstract > T shadow$getValue(Property property); @Shadow public abstract , V extends T> C shadow$setValue(Property property, V value); @Shadow public abstract > C shadow$cycle(Property property); - @Shadow public abstract Map, Comparable> shadow$getValues(); + @Shadow public abstract Stream> shadow$getValues(); // @formatter:on @SuppressWarnings("unchecked") @@ -79,7 +79,7 @@ public abstract class StateHolderMixin_API, C> implements Sta private , T extends Comparable, V extends T> V api$mapFromApi(final StateProperty stateProperty, final ApiT value) { if (value instanceof final Axis axis) { - return (V) AxisUtil.getFor(axis); + return (V) (Object) AxisUtil.getFor(axis); } if (value instanceof final org.spongepowered.api.util.Direction dir) { final V mappedValue = (V) DirectionUtil.getFor(dir); @@ -152,16 +152,16 @@ public > Optional cycleValue(final Key> stateProperties() { - return (Collection) this.shadow$getValues().keySet(); + return (Collection) this.shadow$getValues().map(Property.Value::property).collect(Collectors.toList()); } @Override public Collection statePropertyValues() { - return this.shadow$getValues().values().stream().map(this::api$mapToApi).toList(); + return this.shadow$getValues().map(v -> this.api$mapToApi(v.value())).toList(); } @Override - public Map, ?> statePropertyMap() { - return (Map) this.shadow$getValues().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> this.api$mapToApi(e.getValue()))); + public java.util.Map, ?> statePropertyMap() { + return (java.util.Map) this.shadow$getValues().collect(Collectors.toMap(v -> v.property(), v -> this.api$mapToApi(v.value()))); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/NoteBlockInstrumentMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/NoteBlockInstrumentMixin_API.java index 5686585dcdb..ae1a8fae1be 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/NoteBlockInstrumentMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/NoteBlockInstrumentMixin_API.java @@ -41,6 +41,6 @@ public abstract class NoteBlockInstrumentMixin_API implements InstrumentType { @Override public SoundType sound() { - return (SoundType) this.shadow$getSoundEvent().value(); + return (SoundType) (Object) this.shadow$getSoundEvent().value(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/GenerationStep_CarvingMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/PotentSulfurStateMixin_API.java similarity index 84% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/GenerationStep_CarvingMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/PotentSulfurStateMixin_API.java index cbc9ad223ce..97152fdcbaa 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/GenerationStep_CarvingMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/PotentSulfurStateMixin_API.java @@ -22,13 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.level.levelgen; +package org.spongepowered.common.mixin.api.minecraft.world.level.block.state.properties; -import net.minecraft.world.level.levelgen.GenerationStep; -import org.spongepowered.api.world.generation.carver.CarvingStep; +import net.minecraft.world.level.block.state.properties.PotentSulfurState; import org.spongepowered.asm.mixin.Mixin; -@Mixin(GenerationStep.Carving.class) -public abstract class GenerationStep_CarvingMixin_API implements CarvingStep { +@Mixin(PotentSulfurState.class) +public abstract class PotentSulfurStateMixin_API implements org.spongepowered.api.data.type.PotentSulfurState { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/DripstoneThicknessMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/SpeleothemThicknessMixin_API.java similarity index 88% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/DripstoneThicknessMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/SpeleothemThicknessMixin_API.java index 59f0823a809..58dd924adaf 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/DripstoneThicknessMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/SpeleothemThicknessMixin_API.java @@ -24,11 +24,11 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level.block.state.properties; -import net.minecraft.world.level.block.state.properties.DripstoneThickness; +import net.minecraft.world.level.block.state.properties.SpeleothemThickness; import org.spongepowered.api.data.type.DripstoneSegment; import org.spongepowered.asm.mixin.Mixin; -@Mixin(DripstoneThickness.class) -public abstract class DripstoneThicknessMixin_API implements DripstoneSegment { +@Mixin(SpeleothemThickness.class) +public abstract class SpeleothemThicknessMixin_API implements DripstoneSegment { } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/EventMixin_Forge.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/TestBlockModeMixin_API.java similarity index 81% rename from forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/EventMixin_Forge.java rename to src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/TestBlockModeMixin_API.java index 475f7c6bf71..36ae2824a06 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/EventMixin_Forge.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/block/state/properties/TestBlockModeMixin_API.java @@ -22,13 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.forge.mixin.core.api.event; +package org.spongepowered.common.mixin.api.minecraft.world.level.block.state.properties; -import org.spongepowered.api.event.Event; +import net.minecraft.world.level.block.state.properties.TestBlockMode; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.forge.launch.bridge.event.SpongeEventBridge_Forge; - -@Mixin(value = Event.class, remap = false) -public interface EventMixin_Forge extends SpongeEventBridge_Forge { +@Mixin(TestBlockMode.class) +public abstract class TestBlockModeMixin_API implements org.spongepowered.api.data.type.TestBlockMode { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/border/WorldBorderMixin_Settings_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/border/WorldBorderMixin_Settings_API.java index 8e4dd5558ec..0eebe6ecb3f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/border/WorldBorderMixin_Settings_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/border/WorldBorderMixin_Settings_API.java @@ -25,64 +25,69 @@ package org.spongepowered.common.mixin.api.minecraft.world.level.border; import net.minecraft.world.level.border.WorldBorder; +import org.spongepowered.api.util.Ticks; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.util.SpongeTicks; import org.spongepowered.math.vector.Vector2d; -import java.time.Duration; @Mixin(WorldBorder.Settings.class) +@Implements(@Interface(iface = org.spongepowered.api.world.border.WorldBorder.class, prefix = "api$")) public abstract class WorldBorderMixin_Settings_API implements org.spongepowered.api.world.border.WorldBorder { //@formatter:off - @Shadow public abstract double shadow$getCenterX(); - @Shadow public abstract double shadow$getCenterZ(); - @Shadow public abstract double shadow$getDamagePerBlock(); - @Shadow public abstract double shadow$getSafeZone(); - @Shadow public abstract int shadow$getWarningBlocks(); - @Shadow public abstract int shadow$getWarningTime(); - @Shadow public abstract double shadow$getSize(); - @Shadow public abstract long shadow$getSizeLerpTime(); - @Shadow public abstract double shadow$getSizeLerpTarget(); + @Shadow public abstract double shadow$centerX(); + @Shadow public abstract double shadow$centerZ(); + @Shadow public abstract double shadow$damagePerBlock(); + @Shadow public abstract double shadow$safeZone(); + @Shadow public abstract int shadow$warningBlocks(); + @Shadow public abstract int shadow$warningTime(); + @Shadow public abstract double shadow$size(); + @Shadow public abstract long shadow$lerpTime(); + @Shadow public abstract double shadow$lerpTarget(); //@formatter:on @Override public Vector2d center() { - return new Vector2d(this.shadow$getCenterX(), this.shadow$getCenterZ()); + return new Vector2d(this.shadow$centerX(), this.shadow$centerZ()); } @Override public double targetDiameter() { - return this.shadow$getSizeLerpTarget(); + return this.shadow$lerpTarget(); } @Override - public Duration timeUntilTargetDiameter() { - return Duration.ofMillis(this.shadow$getSizeLerpTime()); + public Ticks timeUntilTargetDiameter() { + return SpongeTicks.ticksOrInfinite(this.shadow$lerpTime()); } @Override public double diameter() { - return this.shadow$getSize(); + return this.shadow$size(); } - @Override - public double safeZone() { - return this.shadow$getSafeZone(); + @Intrinsic + public double api$safeZone() { + return this.shadow$safeZone(); } - @Override - public double damagePerBlock() { - return this.shadow$getDamagePerBlock(); + @Intrinsic + public double api$damagePerBlock() { + return this.shadow$damagePerBlock(); } - @Override - public Duration warningTime() { - return Duration.ofMillis(this.shadow$getWarningTime()); + @Intrinsic + public Ticks api$warningTime() { + return SpongeTicks.ticksOrInfinite(this.shadow$warningTime()); } @Override public int warningDistance() { - return this.shadow$getWarningBlocks(); + return this.shadow$warningBlocks(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/ChunkAccessMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/ChunkAccessMixin_API.java index 10ea2b47b1b..53b02e8989d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/ChunkAccessMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/ChunkAccessMixin_API.java @@ -67,7 +67,7 @@ public abstract class ChunkAccessMixin_API

> implements Chunk< @Shadow public abstract ChunkPos shadow$getPos(); @Shadow public abstract int shadow$getHeight(Heightmap.Types var1, int var2, int var3); @Shadow public abstract LevelChunkSection shadow$getSection(int p_187657_); - @Shadow public abstract void shadow$setUnsaved(boolean p_62094_); + @Shadow public abstract void shadow$markUnsaved(); // @formatter:on @SuppressWarnings("unchecked") @Override @@ -96,8 +96,8 @@ public boolean isEmpty() { @Override public boolean setBiome(final int x, final int y, final int z, final Biome biome) { - return VolumeStreamUtils.setBiomeOnNativeChunk(x, y, z, this.getMinBuildHeight(), this.getHeight(), - biome, i -> this.shadow$getSection(this.getSectionIndex(i)), () -> this.shadow$setUnsaved(true)); + return VolumeStreamUtils.setBiomeOnNativeChunk(x, y, z, this.getMinY(), this.getHeight(), + biome, i -> this.shadow$getSection(this.getSectionIndex(i)), this::shadow$markUnsaved); } @Override @@ -117,7 +117,7 @@ public void setInhabitedTime(final Ticks newInhabitedTime) { @Override public Vector3i chunkPosition() { final ChunkPos chunkPos = this.shadow$getPos(); - return new Vector3i(chunkPos.x, 0, chunkPos.z); + return new Vector3i(chunkPos.x(), 0, chunkPos.z()); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/EmptyLevelChunkMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/EmptyLevelChunkMixin_API.java index 3c2c72817b3..6d8553e12a7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/EmptyLevelChunkMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/EmptyLevelChunkMixin_API.java @@ -24,13 +24,14 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level.chunk; -import net.minecraft.core.Registry; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.chunk.EmptyLevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.PalettedContainerFactory; import net.minecraft.world.level.chunk.UpgradeData; import net.minecraft.world.level.levelgen.blending.BlendingData; +import org.jetbrains.annotations.Nullable; import org.spongepowered.api.world.biome.Biome; import org.spongepowered.asm.mixin.Mixin; @@ -38,8 +39,8 @@ public abstract class EmptyLevelChunkMixin_API extends LevelChunkMixin_API { public EmptyLevelChunkMixin_API( - final ChunkPos $$0, final UpgradeData $$1, final LevelHeightAccessor $$2, final Registry $$3, final long $$4, - final LevelChunkSection[] $$5, final BlendingData $$6 + final ChunkPos $$0, final UpgradeData $$1, final LevelHeightAccessor $$2, final PalettedContainerFactory $$3, + final long $$4, final @Nullable LevelChunkSection[] $$5, final @Nullable BlendingData $$6 ) { super($$0, $$1, $$2, $$3, $$4, $$5, $$6); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/LevelChunkMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/LevelChunkMixin_API.java index 45c896ba6cf..ab5313c07a1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/LevelChunkMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/LevelChunkMixin_API.java @@ -27,7 +27,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; -import net.minecraft.util.Tuple; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.ChunkPos; @@ -38,6 +38,7 @@ import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.PalettedContainerFactory; import net.minecraft.world.level.chunk.UpgradeData; import net.minecraft.world.level.entity.EntitySection; import net.minecraft.world.level.entity.EntitySectionStorage; @@ -57,6 +58,7 @@ import org.spongepowered.api.util.AABB; import org.spongepowered.api.util.PositionOutOfBoundsException; import org.spongepowered.api.util.Ticks; +import org.spongepowered.api.util.Tuple; import org.spongepowered.api.world.BlockChangeFlag; import org.spongepowered.api.world.HeightTypes; import org.spongepowered.api.world.WorldLike; @@ -122,8 +124,8 @@ public abstract class LevelChunkMixin_API extends ChunkAccess implements WorldCh private @Nullable SpongeChunkLayout api$chunkLayout; public LevelChunkMixin_API( - final ChunkPos $$0, final UpgradeData $$1, final LevelHeightAccessor $$2, final Registry $$3, final long $$4, - final LevelChunkSection[] $$5, final BlendingData $$6 + final ChunkPos $$0, final UpgradeData $$1, final LevelHeightAccessor $$2, final PalettedContainerFactory $$3, final long $$4, + final @Nullable LevelChunkSection[] $$5, final @Nullable BlendingData $$6 ) { super($$0, $$1, $$2, $$3, $$4, $$5, $$6); } @@ -134,7 +136,7 @@ public Palette blockPalette() { return PaletteWrapper.of( PaletteTypes.BLOCK_STATE_PALETTE.get(), Block.BLOCK_STATE_REGISTRY, - (org.spongepowered.api.registry.Registry) this.level.registryAccess().registry(Registries.BLOCK).get() + (org.spongepowered.api.registry.Registry) this.level.registryAccess().lookupOrThrow(Registries.BLOCK) ); } @@ -148,9 +150,9 @@ public Biome biome(final int x, final int y, final int z) { @Override public boolean setBiome(final int x, final int y, final int z, final Biome biome) { - return VolumeStreamUtils.setBiomeOnNativeChunk(x, y, z, this.getMinBuildHeight(), this.getHeight(), + return VolumeStreamUtils.setBiomeOnNativeChunk(x, y, z, this.getMinY(), this.getHeight(), biome, i -> this.getSection(this.getSectionIndex(i)), () -> { - this.setUnsaved(true); + this.markUnsaved(); ((ChunkSourceBridge) this.level.getChunkSource()).bridge$biomeChanged(this.chunkPos); }); @@ -177,19 +179,25 @@ public void setInhabitedTime(final Ticks newInhabitedTime) { @Override public Vector3i chunkPosition() { - return new Vector3i(this.chunkPos.x, 0, this.chunkPos.z); + return new Vector3i(this.chunkPos.x(), 0, this.chunkPos.z()); } @Override public double regionalDifficultyFactor() { - return new DifficultyInstance(this.level.getDifficulty(), this.level.getDayTime(), - this.inhabitedTime().ticks(), this.level.getMoonBrightness()).getEffectiveDifficulty(); + if (!(this.level instanceof ServerLevel sl)) { + return 0; + } + return new DifficultyInstance(this.level.getDifficulty(), this.level.getOverworldClockTime(), + this.inhabitedTime().ticks(), sl.getMoonBrightness(sl.getRespawnData().pos())).getEffectiveDifficulty(); } @Override public double regionalDifficultyPercentage() { - return new DifficultyInstance(this.level.getDifficulty(), this.level.getDayTime(), - this.inhabitedTime().ticks(), this.level.getMoonBrightness()).getSpecialMultiplier(); + if (!(this.level instanceof ServerLevel sl)) { + return 0; + } + return new DifficultyInstance(this.level.getDifficulty(), this.level.getOverworldClockTime(), + this.inhabitedTime().ticks(), sl.getMoonBrightness(sl.getRespawnData().pos())).getSpecialMultiplier(); } @Override @@ -349,7 +357,7 @@ public VolumeStream biomeStream(final Vector3i min, final Vec final Vector3i size = max.sub(min).add(1, 1 ,1); final @MonotonicNonNull ObjectArrayMutableBiomeBuffer backingVolume; if (shouldCarbonCopy) { - final Registry biomeRegistry = this.level.registryAccess().registryOrThrow(Registries.BIOME); + final Registry biomeRegistry = this.level.registryAccess().lookupOrThrow(Registries.BIOME); backingVolume = new ObjectArrayMutableBiomeBuffer(min, size, VolumeStreamUtils.nativeToSpongeRegistry(biomeRegistry)); } else { backingVolume = null; @@ -387,7 +395,7 @@ public int highestYAt(final int x, final int z) { public Vector3i min() { if (this.api$blockMin == null) { if (this.api$chunkLayout == null) { - this.api$chunkLayout = new SpongeChunkLayout(this.level.getMinBuildHeight(), this.level.getHeight()); + this.api$chunkLayout = new SpongeChunkLayout(this.level.getMinY(), this.level.getHeight()); } this.api$blockMin = this.api$chunkLayout.forceToWorld(this.chunkPosition()); } @@ -398,7 +406,7 @@ public Vector3i min() { public Vector3i max() { if (this.api$blockMax == null) { if (this.api$chunkLayout == null) { - this.api$chunkLayout = new SpongeChunkLayout(this.level.getMinBuildHeight(), this.level.getHeight()); + this.api$chunkLayout = new SpongeChunkLayout(this.level.getMinY(), this.level.getHeight()); } this.api$blockMax = this.min().add(this.api$chunkLayout.chunkSize()).sub(1, 1, 1); } @@ -408,7 +416,7 @@ public Vector3i max() { @Override public Vector3i size() { if (this.api$chunkLayout == null) { - this.api$chunkLayout = new SpongeChunkLayout(this.level.getMinBuildHeight(), this.level.getHeight()); + this.api$chunkLayout = new SpongeChunkLayout(this.level.getMinY(), this.level.getHeight()); } return this.api$chunkLayout.chunkSize(); } @@ -433,7 +441,7 @@ public Optional entity(final UUID uuid) { public Collection entities() { final PersistentEntitySectionManager entityManager = ((ServerLevelAccessor) this.level).accessor$getEntityManager(); final EntitySectionStorage entitySectionStorage = ((PersistentEntitySectionManagerAccessor) entityManager).accessor$sectionStorage(); - return (Collection) entitySectionStorage.getExistingSectionsInChunk(this.chunkPos.toLong()).flatMap(EntitySection::getEntities).toList(); + return (Collection) entitySectionStorage.getExistingSectionsInChunk(this.chunkPos.pack()).flatMap(EntitySection::getEntities).toList(); } @SuppressWarnings({"rawtypes", "unchecked"}) diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/ProtoChunkMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/ProtoChunkMixin_API.java index 963fa889795..cb30dd1860f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/ProtoChunkMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/chunk/ProtoChunkMixin_API.java @@ -26,13 +26,13 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; -import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.PalettedContainerFactory; import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.UpgradeData; import net.minecraft.world.level.chunk.status.ChunkStatus; @@ -86,8 +86,8 @@ public abstract class ProtoChunkMixin_API extends ChunkAccess implements Generat private @Nullable Vector3i api$blockMax; public ProtoChunkMixin_API( - final ChunkPos $$0, final UpgradeData $$1, final LevelHeightAccessor $$2, final Registry $$3, final long $$4, - final LevelChunkSection[] $$5, final BlendingData $$6 + final ChunkPos $$0, final UpgradeData $$1, final LevelHeightAccessor $$2, final PalettedContainerFactory $$3, + final long $$4, final LevelChunkSection[] $$5, final BlendingData $$6 ) { super($$0, $$1, $$2, $$3, $$4, $$5, $$6); } @@ -148,7 +148,7 @@ public boolean setBiome(final int x, final int y, final int z, final Biome biome if (!this.shadow$getPersistedStatus().isOrAfter(ChunkStatus.BIOMES) && (this.belowZeroRetrogen == null || !this.belowZeroRetrogen.targetStatus().isOrAfter(ChunkStatus.BIOMES))) { throw new IllegalStateException("Asking for biomes before we have biomes"); } - return VolumeStreamUtils.setBiomeOnNativeChunk(x, y, z, this.getMinBuildHeight(), this.getHeight(), + return VolumeStreamUtils.setBiomeOnNativeChunk(x, y, z, this.getMinY(), this.getHeight(), biome, i -> this.getSection(this.getSectionIndex(i)), () -> {}); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/dimension/DimensionTypeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/dimension/DimensionTypeMixin_API.java index 31356c536b5..c59ccf7f553 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/dimension/DimensionTypeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/dimension/DimensionTypeMixin_API.java @@ -26,16 +26,20 @@ import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.dimension.DimensionType; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.service.context.Context; import org.spongepowered.api.world.WorldType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.data.holder.SpongeDataHolder; +import org.spongepowered.common.util.DataPackUtil; import java.util.Objects; +import java.util.Optional; @Mixin(DimensionType.class) public abstract class DimensionTypeMixin_API implements WorldType, SpongeDataHolder { @@ -53,9 +57,14 @@ public Context context() { } @Nullable - private ResourceLocation api$location() { + private Identifier api$location() { final Registry registry = SpongeCommon.vanillaRegistry(Registries.DIMENSION_TYPE); return registry.getKey((DimensionType) (Object) this); } + + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, DimensionType.DIRECT_CODEC, (DimensionType) (Object) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/dimension/LevelStemMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/dimension/LevelStemMixin_API.java new file mode 100644 index 00000000000..e8b0d2a19de --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/dimension/LevelStemMixin_API.java @@ -0,0 +1,64 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.level.dimension; + +import net.minecraft.core.Holder; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.world.WorldType; +import org.spongepowered.api.world.generation.ChunkGenerator; +import org.spongepowered.api.world.server.WorldArchetypeType; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.util.DataPackUtil; + +import java.util.Optional; + +@Mixin(LevelStem.class) +public abstract class LevelStemMixin_API implements WorldArchetypeType { + + // @formatter:off + @Shadow @Final private Holder type; + @Shadow @Final private net.minecraft.world.level.chunk.ChunkGenerator generator; + // @formatter:on + + @Override + public WorldType worldType() { + return (WorldType) (Object) this.type.value(); + } + + @Override + public ChunkGenerator chunkGenerator() { + return (ChunkGenerator) this.generator; + } + + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, LevelStem.CODEC, (LevelStem) (Object) this); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/DensityFunctionMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/DensityFunctionMixin_API.java index 2e4cd9244f8..b0858f95717 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/DensityFunctionMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/DensityFunctionMixin_API.java @@ -25,10 +25,16 @@ package org.spongepowered.common.mixin.api.minecraft.world.level.levelgen; import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.DensityFunctions; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.util.DataPackUtil; import org.spongepowered.math.vector.Vector3i; +import java.util.Optional; + @Mixin(DensityFunction.class) public interface DensityFunctionMixin_API extends org.spongepowered.api.world.generation.config.noise.DensityFunction { @@ -57,4 +63,9 @@ default double compute(final Vector3i pos) { default double compute(final int x, final int y, final int z) { return this.shadow$compute(new DensityFunction.SinglePointContext(x, y, z)); } + + @Override + default Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, DensityFunctions.DIRECT_CODEC, (DensityFunction) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NoiseGeneratorSettingsMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NoiseGeneratorSettingsMixin_API.java index baee3cb40a7..d8f11aad61b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NoiseGeneratorSettingsMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NoiseGeneratorSettingsMixin_API.java @@ -30,6 +30,8 @@ import net.minecraft.world.level.levelgen.NoiseSettings; import net.minecraft.world.level.levelgen.SurfaceRules; import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.world.biome.BiomeAttributes; import org.spongepowered.api.world.generation.config.SurfaceRule; import org.spongepowered.api.world.generation.config.noise.NoiseConfig; @@ -41,8 +43,10 @@ import org.spongepowered.asm.mixin.Intrinsic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.util.DataPackUtil; import java.util.List; +import java.util.Optional; @Mixin(NoiseGeneratorSettings.class) @Implements(@Interface(iface = NoiseGeneratorConfig.class, prefix = "noiseGeneratorConfig$", remap = Remap.NONE)) @@ -116,4 +120,9 @@ public boolean mobGeneration() { public SurfaceRule surfaceRule() { return (SurfaceRule) this.surfaceRule; } + + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, NoiseGeneratorSettings.DIRECT_CODEC, (NoiseGeneratorSettings) (Object) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NoiseRouterMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NoiseRouterMixin_API.java index c5831f8c3c5..39b4796b568 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NoiseRouterMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NoiseRouterMixin_API.java @@ -44,7 +44,6 @@ public abstract class NoiseRouterMixin_API implements org.spongepowered.api.worl @Shadow @Final private net.minecraft.world.level.levelgen.DensityFunction erosion; @Shadow @Final private net.minecraft.world.level.levelgen.DensityFunction depth; @Shadow @Final private net.minecraft.world.level.levelgen.DensityFunction ridges; - @Shadow @Final private net.minecraft.world.level.levelgen.DensityFunction initialDensityWithoutJaggedness; @Shadow @Final private net.minecraft.world.level.levelgen.DensityFunction finalDensity; @Shadow @Final private net.minecraft.world.level.levelgen.DensityFunction veinToggle; @Shadow @Final private net.minecraft.world.level.levelgen.DensityFunction veinRidged; @@ -101,11 +100,6 @@ public DensityFunction ridges() { return (DensityFunction) this.ridges; } - @Override - public DensityFunction initialDensityWithoutJaggedness() { - return (DensityFunction) this.initialDensityWithoutJaggedness; - } - @Override public DensityFunction finalDensity() { return (DensityFunction) this.finalDensity; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NormalNoise_NoiseParametersMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NormalNoise_NoiseParametersMixin_API.java index 73e92ecb3d8..b9405ddf57a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NormalNoise_NoiseParametersMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/NormalNoise_NoiseParametersMixin_API.java @@ -26,12 +26,16 @@ import it.unimi.dsi.fastutil.doubles.DoubleList; import net.minecraft.world.level.levelgen.synth.NormalNoise; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.world.generation.config.noise.Noise; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.util.DataPackUtil; import java.util.List; +import java.util.Optional; @Mixin(NormalNoise.NoiseParameters.class) public abstract class NormalNoise_NoiseParametersMixin_API implements Noise { @@ -50,4 +54,9 @@ public int octave() { public List amplitudes() { return this.amplitudes; } + + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, NormalNoise.NoiseParameters.DIRECT_CODEC, (NormalNoise.NoiseParameters) (Object) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/carver/ConfiguredWorldCarverMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/carver/ConfiguredWorldCarverMixin_API.java index b8deff29e63..c3012dc7ca8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/carver/ConfiguredWorldCarverMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/carver/ConfiguredWorldCarverMixin_API.java @@ -24,22 +24,20 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level.levelgen.carver; -import com.google.gson.JsonElement; -import com.mojang.serialization.Codec; import net.minecraft.world.level.levelgen.carver.CarverConfiguration; import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; import net.minecraft.world.level.levelgen.carver.WorldCarver; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.world.generation.carver.Carver; import org.spongepowered.api.world.generation.carver.CarverType; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.world.generation.carver.SpongeCarverTemplate; +import org.spongepowered.common.util.DataPackUtil; + +import java.util.Optional; -import java.io.IOException; @Mixin(ConfiguredWorldCarver.class) public abstract class ConfiguredWorldCarverMixin_API implements Carver { @@ -54,13 +52,9 @@ public CarverType type() { return (CarverType) this.worldCarver; } + @SuppressWarnings("unchecked") @Override - public DataView toContainer() { - final JsonElement serialized = SpongeCarverTemplate.encode((Codec>) (Object) this.worldCarver.configuredCodec(), (ConfiguredWorldCarver) (Object) this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Carver: " + serialized, e); - } + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, ConfiguredWorldCarver.DIRECT_CODEC, (ConfiguredWorldCarver) (Object) this); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/carver/WorldCarverMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/carver/WorldCarverMixin_API.java index e0105288e0e..99201607945 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/carver/WorldCarverMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/carver/WorldCarverMixin_API.java @@ -24,39 +24,12 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level.levelgen.carver; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; import net.minecraft.world.level.levelgen.carver.CarverConfiguration; -import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; import net.minecraft.world.level.levelgen.carver.WorldCarver; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.world.generation.carver.Carver; import org.spongepowered.api.world.generation.carver.CarverType; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.world.generation.carver.SpongeCarverTemplate; - -import java.io.IOException; @Mixin(WorldCarver.class) public abstract class WorldCarverMixin_API implements CarverType { - //@formatter:off - @Shadow @Final private MapCodec> configuredCodec; - //@formatter:on - - @Override - public Carver configure(final DataView config) throws IllegalArgumentException { - try { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(config)); - return (Carver) (Object) SpongeCarverTemplate.decode((Codec>) (Object) this.configuredCodec.codec(), json, SpongeCommon.server().registryAccess()); - } catch (IOException e) { - throw new IllegalStateException("Could not read configuration: " + config, e); - } - } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/feature/ConfiguredFeatureMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/feature/ConfiguredFeatureMixin_API.java index 3226480c026..8aa1a19924f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/feature/ConfiguredFeatureMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/feature/ConfiguredFeatureMixin_API.java @@ -33,8 +33,10 @@ import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; +import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.data.persistence.DataFormats; import org.spongepowered.api.data.persistence.DataView; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.world.generation.feature.Feature; import org.spongepowered.api.world.generation.feature.FeatureType; import org.spongepowered.api.world.server.ServerLocation; @@ -43,10 +45,12 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.util.DataPackUtil; import org.spongepowered.common.util.VecHelper; import org.spongepowered.math.vector.Vector3i; import java.io.IOException; +import java.util.Optional; @Mixin(ConfiguredFeature.class) public abstract class ConfiguredFeatureMixin_API< @@ -86,4 +90,10 @@ public boolean place(ServerWorld world, Vector3i pos) { public boolean place(final ServerLocation location) { return this.place(location.world(), location.blockPosition()); } + + @SuppressWarnings("unchecked") + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, ConfiguredFeature.DIRECT_CODEC, (ConfiguredFeature) (Object) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/placement/PlacedFeatureMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/placement/PlacedFeatureMixin_API.java index 57948bd01be..eff1e4bb02e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/placement/PlacedFeatureMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/placement/PlacedFeatureMixin_API.java @@ -30,6 +30,8 @@ import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.world.generation.feature.Feature; import org.spongepowered.api.world.generation.feature.FeatureType; import org.spongepowered.api.world.generation.feature.PlacementModifier; @@ -38,10 +40,12 @@ import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.util.DataPackUtil; import org.spongepowered.common.util.VecHelper; import org.spongepowered.math.vector.Vector3i; import java.util.List; +import java.util.Optional; @Mixin(PlacedFeature.class) public abstract class PlacedFeatureMixin_API implements org.spongepowered.api.world.generation.feature.PlacedFeature { @@ -71,4 +75,9 @@ public boolean place(final ServerWorld world, final Vector3i pos) { public boolean place(final ServerLocation location) { return this.place(location.world(), location.blockPosition()); } + + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, PlacedFeature.DIRECT_CODEC, (PlacedFeature) (Object) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureMixin_API.java index 9e057d9f93d..9f7163ca9c8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureMixin_API.java @@ -30,6 +30,7 @@ import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.core.SectionPos; +import net.minecraft.core.registries.Registries; import net.minecraft.resources.RegistryOps; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; @@ -41,9 +42,11 @@ import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureSpawnOverride; import net.minecraft.world.level.levelgen.structure.StructureStart; +import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.data.persistence.DataFormats; import org.spongepowered.api.data.persistence.DataView; import org.spongepowered.api.entity.EntityCategory; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.world.generation.feature.DecorationStep; import org.spongepowered.api.world.generation.structure.StructureType; import org.spongepowered.api.world.server.ServerLocation; @@ -51,12 +54,14 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.util.DataPackUtil; import org.spongepowered.common.util.VecHelper; import org.spongepowered.math.vector.Vector3i; import java.io.IOException; import java.util.Collection; import java.util.Map; +import java.util.Optional; @Mixin(Structure.class) public abstract class StructureMixin_API implements org.spongepowered.api.world.generation.structure.Structure { @@ -73,8 +78,20 @@ public boolean place(final ServerWorld world, final Vector3i pos) { // see PlaceCommand#placeStructure final ServerLevel level = (ServerLevel) world; final ServerChunkCache chunkSource = level.getChunkSource(); - final StructureStart start = ((Structure) (Object) this).generate(level.registryAccess(), chunkSource.getGenerator(), chunkSource.getGenerator().getBiomeSource(), - chunkSource.randomState(), level.getStructureManager(), level.getSeed(), new ChunkPos(VecHelper.toBlockPos(pos)), 0, level, b -> true); + final var key = level.registryAccess().lookupOrThrow(Registries.STRUCTURE).wrapAsHolder((Structure) (Object) this); + final StructureStart start = ((Structure) (Object) this).generate( + key, + level.dimension(), + level.registryAccess(), + chunkSource.getGenerator(), + chunkSource.getGenerator().getBiomeSource(), + chunkSource.randomState(), + level.getStructureManager(), + level.getSeed(), + ChunkPos.containing(VecHelper.toBlockPos(pos)), + 0, + level, + ignored -> true); if (!start.isValid()) { return false; @@ -87,7 +104,7 @@ public boolean place(final ServerWorld world, final Vector3i pos) { return false; } ChunkPos.rangeClosed(minPos, maxPos).forEach((chunkPos) -> start.placeInChunk(level, level.structureManager(), chunkSource.getGenerator(), level.getRandom(), - new BoundingBox(chunkPos.getMinBlockX(), level.getMinBuildHeight(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), level.getMaxBuildHeight(), chunkPos.getMaxBlockZ()), chunkPos)); + new BoundingBox(chunkPos.getMinBlockX(), level.getMinY(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), level.getMaxY(), chunkPos.getMaxBlockZ()), chunkPos)); return true; } @@ -131,4 +148,9 @@ public DataView toContainer() { final var type = (net.minecraft.world.level.levelgen.structure.StructureType) this.shadow$type(); return type.codec().codec(); } + + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, Structure.DIRECT_CODEC, (Structure) (Object) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureSetMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureSetMixin_API.java index 8b3ada0c98f..4ac59ca1519 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureSetMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureSetMixin_API.java @@ -25,14 +25,18 @@ package org.spongepowered.common.mixin.api.minecraft.world.level.levelgen.structure; import net.minecraft.world.level.levelgen.structure.StructureSet; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.util.weighted.WeightedTable; import org.spongepowered.api.world.generation.structure.Structure; import org.spongepowered.api.world.generation.structure.StructurePlacement; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.util.DataPackUtil; import java.util.List; +import java.util.Optional; @Mixin(StructureSet.class) public abstract class StructureSetMixin_API implements org.spongepowered.api.world.generation.structure.StructureSet { @@ -55,4 +59,9 @@ public WeightedTable structures() { public StructurePlacement placement() { return (StructurePlacement) this.placement; } + + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, StructureSet.DIRECT_CODEC, (StructureSet) (Object) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureSpawnOverrideMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureSpawnOverrideMixin_API.java index 72ada58616a..26537c3715f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureSpawnOverrideMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/StructureSpawnOverrideMixin_API.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level.levelgen.structure; -import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.util.random.WeightedList; import net.minecraft.world.level.biome.MobSpawnSettings; import net.minecraft.world.level.levelgen.structure.StructureSpawnOverride; import org.spongepowered.api.world.biome.spawner.NaturalSpawner; @@ -32,6 +32,7 @@ import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.data.provider.world.biome.BiomeData; import java.util.List; @@ -40,7 +41,7 @@ public abstract class StructureSpawnOverrideMixin_API implements StructureNatura // @formatter:off @Shadow @Final private StructureSpawnOverride.BoundingBoxType boundingBox; - @Shadow @Final private WeightedRandomList spawns; + @Shadow @Final private WeightedList spawns; // @formatter:on @Override @@ -50,6 +51,6 @@ public boolean full() { @Override public List spawners() { - return (List) this.spawns.unwrap(); + return this.spawns.unwrap().stream().map(BiomeData.WeightedSpanwer::new).toList(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/pools/StructureTemplatePoolMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/pools/StructureTemplatePoolMixin_API.java index fe1eb34bdb1..3ec26a852e0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/pools/StructureTemplatePoolMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/pools/StructureTemplatePoolMixin_API.java @@ -28,14 +28,18 @@ import net.minecraft.core.Holder; import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.util.weighted.WeightedTable; import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPool; import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPoolElement; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.util.DataPackUtil; import java.util.List; +import java.util.Optional; @Mixin(StructureTemplatePool.class) public abstract class StructureTemplatePoolMixin_API implements JigsawPool { @@ -58,4 +62,9 @@ public WeightedTable elements() { } return weightedTable; } + + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, StructureTemplatePool.DIRECT_CODEC, (StructureTemplatePool) (Object) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureProcessorListMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureProcessorListMixin_API.java index b7a829a3658..658bb9d26b7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureProcessorListMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureProcessorListMixin_API.java @@ -26,13 +26,18 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.world.generation.structure.jigsaw.Processor; import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorList; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.util.DataPackUtil; import java.util.List; +import java.util.Optional; @Mixin(StructureProcessorList.class) public abstract class StructureProcessorListMixin_API implements ProcessorList { @@ -45,4 +50,9 @@ public abstract class StructureProcessorListMixin_API implements ProcessorList { public List processors() { return (List) this.list; } + + @Override + public Optional toDataPack(final RegistryHolder registryHolder) { + return DataPackUtil.toDataContainer(registryHolder, StructureProcessorType.LIST_OBJECT_CODEC, (StructureProcessorList) (Object) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureProcessorMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureProcessorMixin_API.java index c30a3c57d88..9b100459ba5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureProcessorMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureProcessorMixin_API.java @@ -26,13 +26,14 @@ import com.google.gson.JsonElement; import com.mojang.serialization.JsonOps; -import net.minecraft.resources.RegistryOps; +import com.mojang.serialization.MapCodec; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.data.persistence.DataFormats; import org.spongepowered.api.world.generation.structure.jigsaw.Processor; -import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.SpongeCommon; @@ -40,25 +41,35 @@ import java.io.IOException; @Mixin(StructureProcessor.class) -public abstract class StructureProcessorMixin_API implements Processor { +public interface StructureProcessorMixin_API extends Processor { // @formatter:off - @Shadow protected abstract

StructureProcessorType

shadow$getType(); + @Shadow MapCodec codec(); // @formatter:on @Override - public ProcessorType type() { - return (ProcessorType) this.shadow$getType(); + @SuppressWarnings({"unchecked", "rawtypes"}) + default ResourceKey type() { + final Identifier api$location = SpongeCommon.server().registryAccess() + .lookupOrThrow(Registries.STRUCTURE_PROCESSOR) + .getKey((MapCodec) this.codec()); + if (api$location == null) { + throw new IllegalStateException("Processor codec is not registered: " + this.codec()); + } + return (ResourceKey) (Object) api$location; } @Override - public DataContainer toContainer() { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, SpongeCommon.server().registryAccess()); - final JsonElement serialized = this.shadow$getType().codec().codec().encodeStart(ops, (StructureProcessor) (Object) this).getOrThrow(); + @SuppressWarnings({"unchecked", "rawtypes"}) + default DataContainer toContainer() { + final MapCodec api$codec = this.codec(); + final JsonElement api$serialized = (JsonElement) api$codec.codec() + .encodeStart(SpongeCommon.server().registryAccess().createSerializationContext(JsonOps.INSTANCE), this) + .getOrThrow(); try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Processor:\n" + serialized, e); + return DataFormats.JSON.get().read(api$serialized.toString()); + } catch (final IOException e) { + throw new IllegalStateException("Could not read deserialized Processor:\n" + api$serialized, e); } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplateMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplateMixin_API.java index 8bbfb8d251c..32e581416bc 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplateMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplateMixin_API.java @@ -27,10 +27,12 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; +import net.minecraft.world.level.storage.TagValueInput; import net.minecraft.world.phys.Vec3; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.BlockType; @@ -53,6 +55,7 @@ import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.data.holder.SpongeArchetypeVolumeDataHolder; import org.spongepowered.common.data.persistence.NBTTranslator; import org.spongepowered.common.util.VecHelper; @@ -196,7 +199,8 @@ public VolumeStream blockEntityArchetypeS // TODO caching? private Stream api$buildEntityArchetypeList() { return this.entityInfoList.stream().map(info -> { - final Optional> by = EntityType.by(info.nbt); + final var input = TagValueInput.create(ProblemReporter.DISCARDING, SpongeCommon.scopedHolder(), info.nbt); + final Optional> by = EntityType.by(input); if (by.isPresent()) { final DataContainer data = NBTTranslator.INSTANCE.translateFrom(info.nbt); final EntityArchetype archetype = EntityArchetype.builder().type((org.spongepowered.api.entity.EntityType) by.get()).entityData(data).build(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/material/FluidMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/material/FluidMixin_API.java index 6b803a1ba1b..97dcf31b3f6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/material/FluidMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/material/FluidMixin_API.java @@ -26,21 +26,16 @@ import com.google.common.collect.ImmutableList; import net.minecraft.core.Holder; -import net.minecraft.tags.TagKey; import net.minecraft.world.level.material.Fluid; import org.spongepowered.api.fluid.FluidState; import org.spongepowered.api.fluid.FluidType; -import org.spongepowered.api.registry.DefaultedRegistryType; -import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.state.StateProperty; -import org.spongepowered.api.tag.Tag; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import java.util.Collection; import java.util.Optional; -import java.util.stream.Collectors; @Mixin(Fluid.class) public abstract class FluidMixin_API implements FluidType { @@ -71,19 +66,4 @@ public Collection> stateProperties() { public Optional> findStateProperty(final String name) { return Optional.ofNullable((StateProperty) this.shadow$getStateDefinition().getProperty(name)); } - - @Override - public DefaultedRegistryType registryType() { - return RegistryTypes.FLUID_TYPE; - } - - @Override - public Collection> tags() { - return this.registryType().get().tags().filter(this::is).collect(Collectors.toSet()); - } - - @Override - public boolean is(final Tag tag) { - return this.builtInRegistryHolder.is((TagKey) (Object) tag); - } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/storage/LevelDataMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/storage/LevelDataMixin_API.java index ce24f3c9beb..327538f9165 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/storage/LevelDataMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/storage/LevelDataMixin_API.java @@ -24,28 +24,19 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level.storage; -import net.minecraft.world.level.GameRules; import net.minecraft.world.level.storage.LevelData; import org.spongepowered.api.util.MinecraftDayTime; -import org.spongepowered.api.world.gamerule.GameRule; import org.spongepowered.api.world.storage.WorldProperties; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.accessor.world.level.GameRulesAccessor; -import org.spongepowered.common.accessor.world.level.GameRules_ValueAccessor; import org.spongepowered.common.util.SpongeMinecraftDayTime; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; @Mixin(LevelData.class) public interface LevelDataMixin_API extends WorldProperties { // @formatter:off @Shadow long shadow$getGameTime(); - @Shadow long shadow$getDayTime(); - @Shadow GameRules shadow$getGameRules(); // @formatter:on @Override @@ -55,51 +46,8 @@ default MinecraftDayTime gameTime() { @Override default MinecraftDayTime dayTime() { - return new SpongeMinecraftDayTime(this.shadow$getDayTime()); + // TODO + return this.gameTime(); } - @Override - default V gameRule(final GameRule gameRule) { - final GameRules.Value value = this.shadow$getGameRules().getRule((GameRules.Key) (Object) Objects.requireNonNull(gameRule, - "gameRule")); - if (value instanceof GameRules.BooleanValue) { - return (V) Boolean.valueOf(((GameRules.BooleanValue) value).get()); - } else if (value instanceof GameRules.IntegerValue) { - return (V) Integer.valueOf(((GameRules.IntegerValue) value).get()); - } - return null; - } - - @Override - default void setGameRule(final GameRule gameRule, final V value) { - Objects.requireNonNull(gameRule, "gameRule"); - Objects.requireNonNull(value, "value"); - - final GameRules.Value mValue = this.shadow$getGameRules().getRule((GameRules.Key) (Object) gameRule); - ((GameRules_ValueAccessor) mValue).invoker$deserialize(value.toString()); - } - - @Override - default Map, ?> gameRules() { - final Map, GameRules.Value> rules = - ((GameRulesAccessor) this.shadow$getGameRules()).accessor$rules(); - - final Map, Object> apiRules = new HashMap<>(); - for (final Map.Entry, GameRules.Value> rule : rules.entrySet()) { - final GameRule key = (GameRule) (Object) rule.getKey(); - final GameRules.Value mValue = rule.getValue(); - Object value = null; - if (mValue instanceof GameRules.BooleanValue) { - value = ((GameRules.BooleanValue) mValue).get(); - } else if (mValue instanceof GameRules.IntegerValue) { - value = ((GameRules.IntegerValue) mValue).get(); - } - - if (value != null) { - apiRules.put(key, value); - } - } - - return apiRules; - } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/storage/ServerLevelDataMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/storage/ServerLevelDataMixin_API.java index 0a0f24d2735..22c2051b23c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/storage/ServerLevelDataMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/storage/ServerLevelDataMixin_API.java @@ -24,7 +24,10 @@ */ package org.spongepowered.common.mixin.api.minecraft.world.level.storage; -import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.clock.WorldClocks; +import net.minecraft.world.level.CustomSpawner; +import net.minecraft.world.level.gamerules.GameRuleMap; +import net.minecraft.world.level.saveddata.WanderingTraderData; import net.minecraft.world.level.storage.ServerLevelData; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; @@ -32,17 +35,23 @@ import org.spongepowered.api.entity.living.trader.WanderingTrader; import org.spongepowered.api.util.MinecraftDayTime; import org.spongepowered.api.util.Ticks; +import org.spongepowered.api.world.gamerule.GameRule; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.api.world.server.storage.ServerWorldProperties; import org.spongepowered.api.world.weather.Weather; import org.spongepowered.api.world.weather.WeatherType; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.accessor.server.MinecraftServerAccessor; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.accessor.server.level.ServerLevelAccessor; +import org.spongepowered.common.accessor.world.entity.npc.wanderingtrader.WanderingTraderSpawnerAccessor; +import org.spongepowered.common.accessor.world.level.GameRulesAccessor; import org.spongepowered.common.bridge.world.level.storage.ServerLevelDataBridge; import org.spongepowered.common.util.Constants; +import org.spongepowered.common.util.SpongeMinecraftDayTime; import org.spongepowered.common.util.SpongeTicks; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.UUID; @@ -52,13 +61,6 @@ public interface ServerLevelDataMixin_API extends ServerWorldProperties { // @formatter:off - @Shadow int shadow$getWanderingTraderSpawnDelay(); - @Shadow void shadow$setWanderingTraderSpawnDelay(int p_230396_1_); - @Shadow int shadow$getWanderingTraderSpawnChance(); - @Shadow void shadow$setWanderingTraderSpawnChance(int p_230397_1_); - @Shadow void shadow$setWanderingTraderId(UUID p_230394_1_); - @Shadow @Nullable UUID shadow$getWanderingTraderId(); - @Shadow void shadow$setDayTime(long p_76068_1_); // @formatter:on @Override @@ -81,40 +83,97 @@ default UUID uniqueId() { return ((ServerLevelDataBridge) this).bridge$spongeData().uniqueId(); } + @Override + default V gameRule(GameRule gameRule) { + final var value = ((ServerLevelDataBridge) this).bridge$level().getGameRules().get((net.minecraft.world.level.gamerules.GameRule) (Object) Objects.requireNonNull(gameRule, + "gameRule")); + return (V) value; + } + + @Override + default void setGameRule(final GameRule gameRule, final V value) { + Objects.requireNonNull(gameRule, "gameRule"); + Objects.requireNonNull(value, "value"); + + ((ServerLevelDataBridge) this).bridge$level().getGameRules().set((net.minecraft.world.level.gamerules.GameRule) (Object) gameRule, value, SpongeCommon.server()); + } + + @Override + default Map, ?> gameRules() { + final GameRuleMap rules = ((GameRulesAccessor) ((ServerLevelDataBridge) this).bridge$level().getGameRules()).accessor$rules(); + final Map, Object> apiRules = new HashMap<>(); + for (final var rule : rules.keySet()) { + final GameRule key = (GameRule) (Object) rule; + apiRules.put(key, rules.get(rule)); + } + return apiRules; + } + @Override default void setDayTime(final MinecraftDayTime dayTime) { - this.shadow$setDayTime(dayTime.asTicks().ticks()); - this.world().ifPresent(w -> ((MinecraftServerAccessor) ((ServerLevel) w).getServer()).invoker$synchronizeTime((ServerLevel) w)); + // TODO - adjust to world clocks now +// this.setClockStates(this.clockStates().clocks()); } + @SuppressWarnings("removal") @Override default Ticks wanderingTraderSpawnDelay() { - return SpongeTicks.ticksOrInfinite(this.shadow$getWanderingTraderSpawnDelay()); + final var data = this.api$wanderingTraderData(); + if (data == null) { + return SpongeTicks.ticksOrInfinite(0); + } + return SpongeTicks.ticksOrInfinite(data.spawnDelay()); } + @SuppressWarnings("removal") @Override default void setWanderingTraderSpawnDelay(final Ticks delay) { - this.shadow$setWanderingTraderSpawnDelay(SpongeTicks.toSaturatedIntOrInfinite(delay)); + final var data = this.api$wanderingTraderData(); + if (data != null) { + data.setSpawnDelay((int) delay.ticks()); + } } + @SuppressWarnings("removal") @Override default int wanderingTraderSpawnChance() { - return this.shadow$getWanderingTraderSpawnChance(); + final var data = this.api$wanderingTraderData(); + if (data == null) { + return 0; + } + return data.spawnChance(); } + @SuppressWarnings("removal") @Override default void setWanderingTraderSpawnChance(final int chance) { - this.shadow$setWanderingTraderSpawnChance(chance); + final var data = this.api$wanderingTraderData(); + if (data != null) { + data.setSpawnChance(chance); + } } + @SuppressWarnings("removal") @Override default void setWanderingTrader(@Nullable final WanderingTrader trader) { - this.shadow$setWanderingTraderId(trader == null ? null : trader.uniqueId()); + // Wandering trader is no longer tracked in WanderingTraderData as of 26.1-snapshot-6 } + @SuppressWarnings("removal") @Override default Optional wanderTraderUniqueId() { - return Optional.ofNullable(this.shadow$getWanderingTraderId()); + // Wandering trader is no longer tracked in WanderingTraderData as of 26.1-snapshot-6 + return Optional.empty(); + } + + private @Nullable WanderingTraderData api$wanderingTraderData() { + final var level = ((ServerLevelDataBridge) this).bridge$level(); + for (final CustomSpawner spawner : ((ServerLevelAccessor) level).accessor$customSpawners()) { + if (spawner instanceof WanderingTraderSpawnerAccessor wtsa) { + return wtsa.invoker$getTraderData(); + } + } + return null; } @Override @@ -127,4 +186,13 @@ default void setWeather(final WeatherType type, final Ticks ticks) { this.offer(Keys.WEATHER, Weather.of(Objects.requireNonNull(type, "type"), Objects.requireNonNull(ticks, "ticks"))); } + @Override + default MinecraftDayTime dayTime() { + final var clockstate = ((ServerLevelDataBridge) this).bridge$level().clockManager().packState().clocks().get(WorldClocks.OVERWORLD); + if (clockstate == null) { + return new SpongeMinecraftDayTime(0); + } + final var totalTicks = clockstate.totalTicks(); + return new SpongeMinecraftDayTime(totalTicks); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/ticks/LevelTicksMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/ticks/LevelTicksMixin_API.java index fe317db6684..24ca4fe3f8a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/ticks/LevelTicksMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/ticks/LevelTicksMixin_API.java @@ -85,7 +85,7 @@ public ScheduledUpdate schedule( final var subCount = level.nextSubTickCount(); final var scheduledUpdate = new ScheduledTick<>( target, blockPos, tickDelay.ticks() + gameTime, (TickPriority) (Object) priority, subCount); - if (this.tickCheck.test(ChunkPos.asLong(blockPos))) { + if (this.tickCheck.test(ChunkPos.pack(blockPos))) { this.shadow$schedule(scheduledUpdate); } return (ScheduledUpdate) (Object) scheduledUpdate; @@ -102,7 +102,7 @@ public Collection> scheduledAt(final int x, final i if (!this.alreadyRunThisTick.isEmpty()) { return Collections.emptySet(); } - final long longPos = ChunkPos.asLong(new BlockPos(x, y, z)); + final long longPos = ChunkPos.pack(new BlockPos(x, y, z)); final LevelChunkTicks $$2 = this.allContainers.get(longPos); return $$2.getAll() diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/service/permission/SubjectMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/service/permission/SubjectMixin_API.java index e50c4b7698e..5cfd85d9854 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/service/permission/SubjectMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/service/permission/SubjectMixin_API.java @@ -26,7 +26,7 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.rcon.RconConsoleSource; -import net.minecraft.world.entity.vehicle.MinecartCommandBlock; +import net.minecraft.world.entity.vehicle.minecart.MinecartCommandBlock; import net.minecraft.world.level.block.entity.CommandBlockEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.service.server.permission.BridgeSubject; diff --git a/src/accessors/java/org/spongepowered/common/accessor/advancements/critereon/MinMaxBounds_DoublesAccessor.java b/src/mixins/java/org/spongepowered/common/mixin/api/tag/TaggableMixin.java similarity index 68% rename from src/accessors/java/org/spongepowered/common/accessor/advancements/critereon/MinMaxBounds_DoublesAccessor.java rename to src/mixins/java/org/spongepowered/common/mixin/api/tag/TaggableMixin.java index 09e8543900a..03a59ff253f 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/advancements/critereon/MinMaxBounds_DoublesAccessor.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/tag/TaggableMixin.java @@ -22,20 +22,21 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.advancements.critereon; +package org.spongepowered.common.mixin.api.tag; -import net.minecraft.advancements.critereon.MinMaxBounds; +import net.minecraft.core.MappedRegistry; +import org.spongepowered.api.registry.DefaultedRegistryType; +import org.spongepowered.api.tag.Tag; +import org.spongepowered.api.tag.Taggable; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; -import org.spongepowered.common.UntransformedInvokerError; -import java.util.Optional; +import java.util.stream.Stream; -@Mixin(MinMaxBounds.Doubles.class) -public interface MinMaxBounds_DoublesAccessor { +@Mixin(value = Taggable.class, remap = false) +public interface TaggableMixin> { - @Invoker("") static MinMaxBounds.Doubles invoker$new(final Optional min, final Optional max) { - throw new UntransformedInvokerError(); + @SuppressWarnings("unchecked") + default Stream> tags(final DefaultedRegistryType registryType) { + return (Stream>) (Object) ((MappedRegistry) registryType.get()).wrapAsHolder((T) this).tags(); } - } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/HolderLookup_ProviderMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/HolderLookup_ProviderMixin.java new file mode 100644 index 00000000000..61f7398dca1 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/HolderLookup_ProviderMixin.java @@ -0,0 +1,70 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core; + +import net.minecraft.core.HolderLookup; +import net.minecraft.resources.Identifier; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.api.registry.ValueNotFoundException; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.accessor.resources.ResourceKeyAccessor; + +import java.util.Optional; +import java.util.stream.Stream; + +@Mixin(value = HolderLookup.Provider.class, priority = 999) +public interface HolderLookup_ProviderMixin extends RegistryHolder { + + @Shadow Optional> shadow$lookup(net.minecraft.resources.ResourceKey> arg); + + @Shadow Stream> shadow$listRegistries(); + + @Override + default Registry registry(final RegistryType type) { + return this.findRegistry(type) + .orElseThrow(() -> new ValueNotFoundException(String.format("No '%s' registry has been defined in root '%s'", type.location(), type.root()))); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + default Optional> findRegistry(final RegistryType type) { + return (Optional) this.shadow$lookup(ResourceKeyAccessor.invoker$create((Identifier) (Object) type.root(), (Identifier) (Object) type.location())); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + default Stream> streamRegistries() { + return (Stream) this.shadow$listRegistries(); + } + + @Override + default Stream> streamRegistries(final ResourceKey root) { + return this.streamRegistries().filter(r -> r.type().root().equals(root)); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/AdvancementMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/advancements/AdvancementMixin.java index 23cabe709b7..fdee5b6c67f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/AdvancementMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/advancements/AdvancementMixin.java @@ -31,9 +31,9 @@ import net.minecraft.advancements.AdvancementRequirements; import net.minecraft.advancements.AdvancementRewards; import net.minecraft.advancements.AdvancementType; -import net.minecraft.advancements.Criterion; import net.minecraft.advancements.DisplayInfo; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.advancements.triggers.Criterion; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spongepowered.api.advancement.criteria.AdvancementCriterion; import org.spongepowered.api.advancement.criteria.AndCriterion; @@ -74,7 +74,7 @@ public abstract class AdvancementMixin implements AdvancementBridge { @SuppressWarnings({"ConstantConditions"}) @Inject(method = "(Ljava/util/Optional;Ljava/util/Optional;Lnet/minecraft/advancements/AdvancementRewards;Ljava/util/Map;Lnet/minecraft/advancements/AdvancementRequirements;Z)V", at = @At("RETURN")) - private void impl$setUpSpongeFields(final Optional parent, final Optional displayInfo, final AdvancementRewards $$2, + private void impl$setUpSpongeFields(final Optional parent, final Optional displayInfo, final AdvancementRewards $$2, final Map> criteria, final AdvancementRequirements requirements, final boolean sendsTelemetryEvent, final CallbackInfo ci) { displayInfo.ifPresent(info -> ((DisplayInfoBridge) info).bridge$setAdvancement((org.spongepowered.api.advancement.Advancement) this)); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/AdvancementProgressMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/advancements/AdvancementProgressMixin.java index 256d15b6fc3..f259e49a37d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/AdvancementProgressMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/advancements/AdvancementProgressMixin.java @@ -28,7 +28,7 @@ import net.minecraft.advancements.AdvancementHolder; import net.minecraft.advancements.AdvancementProgress; import net.minecraft.advancements.AdvancementRequirements; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.PlayerAdvancements; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; @@ -81,7 +81,7 @@ public abstract class AdvancementProgressMixin implements AdvancementProgressBri // @formatter:on @Nullable private Map impl$progressMap; - @Nullable private ResourceLocation impl$advancementKey; + @Nullable private Identifier impl$advancementKey; @Nullable private PlayerAdvancements impl$playerAdvancements; @Override @@ -97,7 +97,7 @@ public abstract class AdvancementProgressMixin implements AdvancementProgressBri } @Override - public ResourceLocation bridge$getAdvancementKey() { + public Identifier bridge$getAdvancementKey() { Preconditions.checkState(this.impl$advancementKey != null, "The advancement is not yet initialized"); return this.impl$advancementKey; } @@ -116,7 +116,7 @@ public abstract class AdvancementProgressMixin implements AdvancementProgressBri } @Override - public void bridge$setAdvancementId(ResourceLocation key) { + public void bridge$setAdvancementId(Identifier key) { Preconditions.checkState(PlatformHooks.INSTANCE.getGeneralHooks().onServerThread()); this.impl$advancementKey = key; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/CriteriaTriggersMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/advancements/CriteriaTriggersMixin.java index 044b4832a6e..8f7ee2e8ac0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/CriteriaTriggersMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/advancements/CriteriaTriggersMixin.java @@ -24,8 +24,8 @@ */ package org.spongepowered.common.mixin.core.advancements; -import net.minecraft.advancements.CriteriaTriggers; -import net.minecraft.advancements.CriterionTrigger; +import net.minecraft.advancements.triggers.CriteriaTriggers; +import net.minecraft.advancements.triggers.CriterionTrigger; import net.minecraft.core.Registry; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/CriterionMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/advancements/CriterionMixin.java index 72b97c688f5..19d7388d229 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/CriterionMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/advancements/CriterionMixin.java @@ -27,9 +27,9 @@ import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; -import net.minecraft.advancements.Criterion; -import net.minecraft.advancements.CriterionTrigger; import net.minecraft.advancements.CriterionTriggerInstance; +import net.minecraft.advancements.triggers.Criterion; +import net.minecraft.advancements.triggers.CriterionTrigger; import net.minecraft.util.ExtraCodecs; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Final; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/CriterionTrigger_ListenerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/advancements/SimpleCriterionTriggerMixin.java similarity index 66% rename from src/mixins/java/org/spongepowered/common/mixin/core/advancements/CriterionTrigger_ListenerMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/advancements/SimpleCriterionTriggerMixin.java index 96f72dc54e4..ee548c29e87 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/advancements/CriterionTrigger_ListenerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/advancements/SimpleCriterionTriggerMixin.java @@ -24,11 +24,13 @@ */ package org.spongepowered.common.mixin.core.advancements; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import io.leangen.geantyref.TypeToken; import net.minecraft.advancements.AdvancementHolder; import net.minecraft.advancements.AdvancementProgress; -import net.minecraft.advancements.CriterionTrigger; import net.minecraft.advancements.CriterionTriggerInstance; +import net.minecraft.advancements.triggers.SimpleCriterionTrigger; import net.minecraft.server.PlayerAdvancements; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.advancement.criteria.AdvancementCriterion; @@ -41,12 +43,8 @@ import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.advancement.CriterionEvent; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.advancement.SpongeCriterionTrigger; import org.spongepowered.common.advancement.SpongeFilteredTrigger; @@ -56,64 +54,61 @@ import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.hooks.PlatformHooks; -@Mixin(CriterionTrigger.Listener.class) -public abstract class CriterionTrigger_ListenerMixin { - - // @formatter:off - @Shadow @Final private CriterionTriggerInstance trigger; - @Shadow @Final private AdvancementHolder advancement; - @Shadow @Final private String criterion; - // @formatter:on +@Mixin(SimpleCriterionTrigger.class) +public abstract class SimpleCriterionTriggerMixin { @SuppressWarnings({"unchecked", "rawtypes"}) - @Inject(method = "run", at = @At("HEAD"), cancellable = true) - private void impl$callEvents(PlayerAdvancements playerAdvancements, CallbackInfo ci) { - final org.spongepowered.api.advancement.Advancement advancement = (org.spongepowered.api.advancement.Advancement) (Object) this.advancement.value(); - AdvancementCriterion advancementCriterion = (AdvancementCriterion) (Object) this.advancement.value().criteria().get(this.criterion); + @WrapOperation(method = "trigger", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/server/PlayerAdvancements;award(Lnet/minecraft/advancements/AdvancementHolder;Ljava/lang/String;)Z")) + private boolean impl$dispatchCriterionEventThenAward(final PlayerAdvancements playerAdvancements, + final AdvancementHolder holder, final String criterionName, final Operation original) { + final org.spongepowered.api.advancement.Advancement advancement = (org.spongepowered.api.advancement.Advancement) (Object) holder.value(); + AdvancementCriterion advancementCriterion = (AdvancementCriterion) (Object) holder.value().criteria().get(criterionName); final CriterionBridge criterionBridge = (CriterionBridge) advancementCriterion; if (criterionBridge.bridge$getScoreCriterion() != null) { advancementCriterion = criterionBridge.bridge$getScoreCriterion(); } - if (!PlatformHooks.INSTANCE.getGeneralHooks().onServerThread()) { - // Some mods do advancement granting on async threads, and we can't allow for the spam to be thrown. - return; + // Some mods do advancement granting on async threads — don't post events from here, but still allow the award. + return original.call(playerAdvancements, holder, criterionName); } - // Sponge filters are always handled in the trigger method + final ServerPlayer player = ((PlayerAdvancementsBridge) playerAdvancements).bridge$getPlayer(); - if (!(this.trigger instanceof SpongeFilteredTrigger)) { - final FilteredTrigger filteredTrigger = (FilteredTrigger) this.trigger; + final CriterionTriggerInstance instance = holder.value().criteria().get(criterionName).triggerInstance(); + + // Sponge filters are handled in SpongeCriterionTrigger.bridge$trigger; only post here for vanilla/mod triggers. + if (!(instance instanceof SpongeFilteredTrigger)) { + final FilteredTrigger filteredTrigger = (FilteredTrigger) instance; final Trigger triggerType = advancementCriterion.type().orElse(null); if (triggerType instanceof SpongeCriterionTrigger) { final Cause cause = PhaseTracker.getInstance().currentCause(); final TypeToken typeToken = (TypeToken) TypeToken.get(triggerType.configurationType()); final CriterionEvent.Trigger event = SpongeEventFactory.createCriterionEventTrigger(cause, - advancement, (ResourceKey) (Object) this.advancement.id(), advancementCriterion, typeToken, player, filteredTrigger, (Trigger) advancementCriterion.type().get(), true); + advancement, (ResourceKey) (Object) holder.id(), advancementCriterion, typeToken, player, filteredTrigger, + (Trigger) advancementCriterion.type().get(), true); SpongeCommon.post(event); if (!event.result()) { - ci.cancel(); - return; + return false; } } } - PhaseTracker.getInstance().pushCause(this.trigger); - // Handle the score criteria ourselves, with each trigger will - // the score be increased by one. - if (advancementCriterion instanceof ScoreAdvancementCriterion sac) { - final AdvancementProgress progress = playerAdvancements.getOrStartProgress(this.advancement); - final var progressMap = ((AdvancementProgressBridge) progress).bridge$getProgressMap(); - if (progressMap.get(sac.name()) instanceof ScoreCriterionProgress score) { - score.add(1); + + PhaseTracker.getInstance().pushCause(instance); + try { + // Score criteria absorb the award and increment progress instead. + if (advancementCriterion instanceof ScoreAdvancementCriterion sac) { + final AdvancementProgress progress = playerAdvancements.getOrStartProgress(holder); + final var progressMap = ((AdvancementProgressBridge) progress).bridge$getProgressMap(); + if (progressMap.get(sac.name()) instanceof ScoreCriterionProgress score) { + score.add(1); + } + return false; } - ci.cancel(); + return original.call(playerAdvancements, holder, criterionName); + } finally { PhaseTracker.getInstance().popCause(); } } - - @Inject(method = "run", at = @At("RETURN")) - private void impl$popCauseAtEndOfEvent(PlayerAdvancements playerAdvancements, CallbackInfo ci) { - PhaseTracker.getInstance().popCause(); - } - } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/adventure/KeyMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/adventure/KeyMixin.java index 44b643db01a..89bff5d64d4 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/adventure/KeyMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/adventure/KeyMixin.java @@ -25,7 +25,7 @@ package org.spongepowered.common.mixin.core.adventure; import net.kyori.adventure.key.Key; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.ResourceKey; import org.spongepowered.asm.mixin.Mixin; @@ -40,6 +40,6 @@ public interface KeyMixin { */ @Overwrite static @NonNull Key key(final @NonNull String namespace, final @NonNull String value) { - return (ResourceKey) (Object) ResourceLocation.fromNamespaceAndPath(namespace, value); + return (ResourceKey) (Object) Identifier.fromNamespaceAndPath(namespace, value); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/adventure/bossbar/HackyBossBarPlatformBridgeMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/adventure/bossbar/HackyBossBarPlatformBridgeMixin.java index 81b02c109e1..6a0aa5e063d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/adventure/bossbar/HackyBossBarPlatformBridgeMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/adventure/bossbar/HackyBossBarPlatformBridgeMixin.java @@ -33,6 +33,8 @@ import org.spongepowered.common.bridge.adventure.BossBarBridge; import org.spongepowered.common.bridge.world.BossEventBridge; +import java.util.UUID; + @Mixin(targets = "net.kyori.adventure.bossbar.HackyBossBarPlatformBridge", remap = false) public abstract class HackyBossBarPlatformBridgeMixin implements BossBarBridge { @@ -42,7 +44,12 @@ public abstract class HackyBossBarPlatformBridgeMixin implements BossBarBridge { public ServerBossEvent bridge$asVanillaServerBar() { if (this.bridge$vanillaServerBar == null) { final BossBar $this = (BossBar) this; - this.bridge$vanillaServerBar = new ServerBossEvent(SpongeAdventure.asVanilla($this.name()), SpongeAdventure.asVanilla($this.color()), SpongeAdventure.asVanilla($this.overlay())); + this.bridge$vanillaServerBar = new ServerBossEvent( + UUID.randomUUID(), + SpongeAdventure.asVanilla($this.name()), + SpongeAdventure.asVanilla($this.color()), + SpongeAdventure.asVanilla($this.overlay()) + ); final BossEventBridge bridge = (BossEventBridge) this.bridge$vanillaServerBar; bridge.bridge$copy($this); bridge.bridge$setAdventure($this); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/adventure/text/AbstractComponentMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/adventure/text/AbstractComponentMixin.java index 91cb9dfb6d2..4eb117a0fde 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/adventure/text/AbstractComponentMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/adventure/text/AbstractComponentMixin.java @@ -31,6 +31,7 @@ import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.adventure.ComponentBridge; +@SuppressWarnings("deprecation") @Mixin(AbstractComponent.class) public abstract class AbstractComponentMixin implements ComponentBridge { private MutableComponent bridge$vanillaComponent; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/adventure/text/format/StyleImplMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/adventure/text/format/StyleImplMixin.java index eee014e0b73..1ed65fb9aab 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/adventure/text/format/StyleImplMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/adventure/text/format/StyleImplMixin.java @@ -47,6 +47,7 @@ public abstract class StyleImplMixin implements StyleBridge { this.bridge$vanilla = StyleAccessor.invoker$new( // color SpongeAdventure.asVanillaNullable($this.color()), + SpongeAdventure.asVanillaNullable($this.shadowColor()), // decorations SpongeAdventure.asVanillaNullable($this.decoration(TextDecoration.BOLD)), SpongeAdventure.asVanillaNullable($this.decoration(TextDecoration.ITALIC)), @@ -59,7 +60,7 @@ public abstract class StyleImplMixin implements StyleBridge { // insertion $this.insertion(), // font - SpongeAdventure.asVanillaNullable($this.font()) + SpongeAdventure.asVanillaFontDescription($this.font()) ); } // Style is immutable, no need to copy diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/block/BedBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/block/BedBlockMixin.java index 97fee76c145..078a6126eae 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/block/BedBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/block/BedBlockMixin.java @@ -49,7 +49,7 @@ public class BedBlockMixin { @Inject(method = "useWithoutItem", at = @At(value = "HEAD"), cancellable = true) private void impl$onUseBed(final BlockState param0, final Level param1, final BlockPos param2, final Player param3, final BlockHitResult param5, final CallbackInfoReturnable cir) { - if (!param1.isClientSide) { + if (!param1.isClientSide()) { final Cause currentCause = PhaseTracker.getInstance().currentCause(); final BlockPos bedLocation = param5.getBlockPos(); final BlockSnapshot snapshot = ((ServerWorld) param1).createSnapshot(bedLocation.getX(), bedLocation.getY(), bedLocation.getZ()); @@ -67,8 +67,8 @@ public class BedBlockMixin { final BlockSnapshot snapshot = ((ServerWorld) param1).createSnapshot(bedLocation.getX(), bedLocation.getY(), bedLocation.getZ()); if (Sponge.eventManager().post(SpongeEventFactory.createSleepingEventFailed(currentCause, snapshot, (Living) param3))) { param3.startSleepInBed(param2).ifLeft((param1x) -> { - if (param1x != null) { - param3.displayClientMessage(param1x.getMessage(), true); + if (param1x != null && param1x.message() != null) { + param3.sendOverlayMessage(param1x.message()); } }); cir.setReturnValue(InteractionResult.SUCCESS); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/block/BlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/block/BlockMixin.java index 5f7545362f6..30b17c6fcc4 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/block/BlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/block/BlockMixin.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.mixin.core.block; +import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.item.ItemStack; @@ -39,23 +40,17 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.TrackableBridge; import org.spongepowered.common.bridge.block.BlockBridge; import org.spongepowered.common.bridge.block.DyeColorBlockBridge; import org.spongepowered.common.event.ShouldFire; import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.util.ReflectionUtil; import org.spongepowered.math.vector.Vector3d; @Mixin(value = Block.class) public abstract class BlockMixin implements BlockBridge, TrackableBridge { - private final boolean impl$isVanilla = this.getClass().getName().startsWith("net.minecraft."); - private final boolean impl$hasCollideLogic = ReflectionUtil.isStepOnDeclared(this.getClass()); - private final boolean impl$hasCollideWithStateLogic = ReflectionUtil.isEntityInsideDeclared(this.getClass()); - /** * We captured the dye color when creating the Block.Properties. * As the Properties objects are discarded we transfer it over to the Block itself now. @@ -75,13 +70,12 @@ public abstract class BlockMixin implements BlockBridge, TrackableBridge { target = "Lnet/minecraft/world/level/block/Block;popResource(Lnet/minecraft/world/level/Level;Ljava/util/function/Supplier;Lnet/minecraft/world/item/ItemStack;)V" ), cancellable = true, - locals = LocalCapture.CAPTURE_FAILSOFT, require = 0, expect = 0 ) private static void impl$throwConstructPreEvent( - final Level level, final BlockPos pos, final ItemStack stack, final CallbackInfo ci, final double $$3, - final double xPos, final double yPos, final double zPos + final Level level, final BlockPos pos, final ItemStack itemStack, final CallbackInfo ci, + @Local(index = 6) final double x, @Local(index = 8) final double y, @Local(index = 10) final double z ) { if (!ShouldFire.CONSTRUCT_ENTITY_EVENT_PRE) { return; @@ -90,7 +84,7 @@ public abstract class BlockMixin implements BlockBridge, TrackableBridge { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(level.getBlockState(pos)); final ConstructEntityEvent.Pre eventPre = SpongeEventFactory.createConstructEntityEventPre( - frame.currentCause(), ServerLocation.of((ServerWorld) level, xPos, yPos, zPos), Vector3d.ZERO, + frame.currentCause(), ServerLocation.of((ServerWorld) level, x, y, z), Vector3d.ZERO, EntityTypes.ITEM.get() ); SpongeCommon.post(eventPre); @@ -106,13 +100,12 @@ public abstract class BlockMixin implements BlockBridge, TrackableBridge { value = "INVOKE", target = "Lnet/minecraft/world/level/block/Block;popResource(Lnet/minecraft/world/level/Level;Ljava/util/function/Supplier;Lnet/minecraft/world/item/ItemStack;)V" ), - cancellable = true, - locals = LocalCapture.CAPTURE_FAILSOFT + cancellable = true ) private static void impl$throwConstructPreEvent( final Level level, final BlockPos pos, final Direction direction, final ItemStack stack, final CallbackInfo ci, - final int stepX, final int stepY, final int stepZ, final double itemWidthX, final double itemWidthZ, - final double xPos, final double yPos, final double zPos, final double xMov, final double yMov, final double zMov + final @Local(index = 12) double x, final @Local(index = 14) double y, final @Local(index = 16) double z, + final @Local(index = 18) double xMov, final @Local(index = 20) double yMov, final @Local(index = 22) double zMov ) { if (!ShouldFire.CONSTRUCT_ENTITY_EVENT_PRE) { return; @@ -121,7 +114,7 @@ public abstract class BlockMixin implements BlockBridge, TrackableBridge { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(level.getBlockState(pos)); final ConstructEntityEvent.Pre eventPre = SpongeEventFactory.createConstructEntityEventPre( - frame.currentCause(), ServerLocation.of((ServerWorld) level, xPos, yPos, zPos), new Vector3d(xMov, yMov, zMov), + frame.currentCause(), ServerLocation.of((ServerWorld) level, x, y, z), new Vector3d(xMov, yMov, zMov), EntityTypes.ITEM.get() ); SpongeCommon.post(eventPre); @@ -131,18 +124,4 @@ public abstract class BlockMixin implements BlockBridge, TrackableBridge { } } - @Override - public boolean bridge$isVanilla() { - return this.impl$isVanilla; - } - - @Override - public boolean bridge$hasCollideLogic() { - return this.impl$hasCollideLogic; - } - - @Override - public boolean bridge$hasCollideWithStateLogic() { - return this.impl$hasCollideWithStateLogic; - } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/client/MinecraftMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/client/MinecraftMixin.java index d5164349642..1dd9c81c207 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/client/MinecraftMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/client/MinecraftMixin.java @@ -56,7 +56,7 @@ public abstract class MinecraftMixin extends ReentrantBlockableEventLoop + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.client.gui.screens.worldselection; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.mojang.datafixers.util.Pair; +import net.minecraft.client.gui.screens.worldselection.WorldOpenFlows; +import net.minecraft.server.ReloadableServerResources; +import net.minecraft.server.WorldLoader; +import net.minecraft.server.packs.resources.CloseableResourceManager; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.world.level.WorldDataConfiguration; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.bridge.server.ReloadableServerResourcesBridge; +import org.spongepowered.common.bridge.server.packs.resources.MultiPackResourceManagerBridge; +import org.spongepowered.common.bridge.server.packs.resources.ResourceManagerBridge; +import org.spongepowered.common.registry.SpongeRegistryHolder; +import org.spongepowered.common.tag.SpongePluginTags; + +@Mixin(WorldOpenFlows.class) +public abstract class WorldOpenFlowsMixin { + + // Since 26.1 this path hands the WorldStem a fresh resource manager instead of the one + // WorldLoader.load created, so the Sponge state established during datapack loading + // (server-scoped plugin registries, services, plugin tags) must carry over to it. + @WrapOperation(method = "createLevelFromExistingSettings", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/server/WorldLoader$PackConfig;createResourceManager()Lcom/mojang/datafixers/util/Pair;")) + private Pair impl$transferSpongeState( + final WorldLoader.PackConfig config, final Operation> original, + final @Local(argsOnly = true) ReloadableServerResources serverResources + ) { + final Pair pair = original.call(config); + final @Nullable ResourceManager originalManager = ((ReloadableServerResourcesBridge) serverResources).bridge$resourceManager(); + final CloseableResourceManager newManager = pair.getSecond(); + if (originalManager != null && originalManager != newManager) { + ((MultiPackResourceManagerBridge) newManager).bridge$setRegistryHolder(((SpongeRegistryHolder) originalManager).registryHolder()); + ((ResourceManagerBridge) newManager).bridge$services(((ResourceManagerBridge) originalManager).bridge$services()); + final @Nullable SpongePluginTags tags = ((ResourceManagerBridge) originalManager).bridge$pluginProvidedTags(); + if (tags != null) { + ((ResourceManagerBridge) newManager).bridge$pluginProvidedTags(tags); + } + } + return pair; + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/client/multiplayer/ClientLevelMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/client/multiplayer/ClientLevelMixin.java index 1803a96b6de..f8e946272b7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/client/multiplayer/ClientLevelMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/client/multiplayer/ClientLevelMixin.java @@ -26,9 +26,10 @@ import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientPacketListener; -import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.extract.LevelExtractor; import net.minecraft.core.Holder; import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.DimensionType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -36,15 +37,15 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.mixin.core.world.level.LevelMixin; -import java.util.function.Supplier; - @Mixin(ClientLevel.class) public abstract class ClientLevelMixin extends LevelMixin { @Inject(method = "", at = @At("TAIL")) - private void impl$onInit(final ClientPacketListener $$0, final ClientLevel.ClientLevelData $$1, final ResourceKey $$2, final Holder $$3, - final int $$4, final int $$5, final Supplier $$6, final LevelRenderer $$7, final boolean $$8, final long $$9, final CallbackInfo ci) { - this.bridge$adjustDimensionLogic($$3.value()); + private void impl$onInit( + final ClientPacketListener listener, final ClientLevel.ClientLevelData data, final ResourceKey key, + final Holder holder, final int storageRange, final int serverSimulationDistance, final LevelExtractor levelExtractor, final boolean isDebug, + final long biomeZoomSeed, final int seaLevel, final CallbackInfo ci) { + this.bridge$adjustDimensionLogic(holder.value()); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/client/network/chat/TranslatableContentsMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/client/network/chat/TranslatableContentsMixin.java index 55e8b462d6c..b1e161ffd6f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/client/network/chat/TranslatableContentsMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/client/network/chat/TranslatableContentsMixin.java @@ -65,7 +65,7 @@ public abstract class TranslatableContentsMixin { // Only do a deep copy if actually necessary if (GlobalTranslator.translator().translate(this.key, actualLocale) != null) { - this.impl$translated = NativeComponentRenderer.apply(toTranslate, actualLocale); + this.impl$translated = NativeComponentRenderer.apply(toTranslate, actualLocale, null); } else { this.impl$translated = toTranslate; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/client/server/IntegratedPlayerListMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/client/server/IntegratedPlayerListMixin.java index 500a95baa45..a7812e8f384 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/client/server/IntegratedPlayerListMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/client/server/IntegratedPlayerListMixin.java @@ -24,9 +24,9 @@ */ package org.spongepowered.common.mixin.core.client.server; -import com.mojang.authlib.GameProfile; import net.minecraft.client.server.IntegratedPlayerList; import net.minecraft.network.chat.Component; +import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.PlayerList; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -42,21 +42,22 @@ public abstract class IntegratedPlayerListMixin extends PlayerListMixin implements IntegratedPlayerListBridge { // @formatter:off - @Shadow public abstract Component shadow$canPlayerLogin(SocketAddress param0, GameProfile param1); + @Shadow public abstract Component shadow$canPlayerLogin(SocketAddress param0, NameAndId param1); // @formatter:on + @Override public CompletableFuture bridge$canPlayerLoginClient(final SocketAddress param0, final com.mojang.authlib.GameProfile param1) { - final Component component = this.shadow$canPlayerLogin(param0, param1); + final var name = new NameAndId(param1); + final Component component = this.shadow$canPlayerLogin(param0, name); if (component == null) { - return this.impl$canPlayerLoginServer(param0, param1); + return this.impl$canPlayerLoginServer(param0, name); } return CompletableFuture.completedFuture(component); } @Redirect(method = "canPlayerLogin", at = @At(value = "INVOKE", - target = "Lnet/minecraft/server/players/PlayerList;canPlayerLogin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)" - + "Lnet/minecraft/network/chat/Component;")) - private Component impl$redirectCanPlayerLoginSuperCall(final PlayerList playerList, final SocketAddress param0, final GameProfile param1) { + target = "Lnet/minecraft/server/players/PlayerList;canPlayerLogin(Ljava/net/SocketAddress;Lnet/minecraft/server/players/NameAndId;)Lnet/minecraft/network/chat/Component;")) + private Component impl$redirectCanPlayerLoginSuperCall(final PlayerList playerList, final SocketAddress param0, final NameAndId param1) { return null; // we'll handle it above which we'll indicate by returning null. } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/commands/CommandFunctionMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/commands/CommandFunctionMixin.java new file mode 100644 index 00000000000..1251b726baa --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/commands/CommandFunctionMixin.java @@ -0,0 +1,60 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.commands; + +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.commands.ExecutionCommandSource; +import net.minecraft.commands.functions.CommandFunction; +import net.minecraft.resources.Identifier; +import org.spongepowered.api.command.CommandCause; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.service.permission.PermissionService; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.bridge.commands.CommandSourceStackBridge; +import org.spongepowered.common.command.brigadier.dispatcher.DelegatingCommandDispatcher; +import org.spongepowered.common.event.tracking.PhaseTracker; + +import java.util.List; + +@Mixin(CommandFunction.class) +public interface CommandFunctionMixin { + + @WrapMethod(method = "fromLines") + private static > CommandFunction impl$onFromLines( + final Identifier $$0, final CommandDispatcher $$1, final T $$2, final List $$3, final Operation> original) { + if ($$1 instanceof final DelegatingCommandDispatcher delegatingCommandDispatcher) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + frame.pushCause(delegatingCommandDispatcher.permissionService().newSubjectReference(PermissionService.SUBJECTS_FUNCTION, $$0.toString()).resolve().join()); + final CommandCause cause = ((CommandSourceStackBridge) $$2).bridge$withCurrentCause(); + ((CommandSourceStackBridge) cause).bridge$permissionService(delegatingCommandDispatcher.permissionService()); + return original.call($$0, $$1, cause, $$3); + } + } else { + return original.call($$0, $$1, $$2, $$3); + } + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/commands/CommandSourceStackMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/commands/CommandSourceStackMixin.java index 245cf278a22..a27740e9733 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/commands/CommandSourceStackMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/commands/CommandSourceStackMixin.java @@ -32,6 +32,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.permissions.PermissionSet; import net.minecraft.util.TaskChainer; import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.Vec2; @@ -44,6 +45,7 @@ import org.spongepowered.api.event.EventContext; import org.spongepowered.api.event.EventContextKey; import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -53,8 +55,9 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.accessor.commands.CommandSourceStackAccessor; -import org.spongepowered.common.accessor.world.entity.EntityAccessor; +import org.spongepowered.common.accessor.world.entity.player.PlayerAccessor; import org.spongepowered.common.bridge.commands.CommandSourceBridge; import org.spongepowered.common.bridge.commands.CommandSourceStackBridge; import org.spongepowered.common.event.tracking.PhaseTracker; @@ -67,11 +70,12 @@ public abstract class CommandSourceStackMixin implements CommandSourceStackBridge { + //@formatter:off @Shadow @Final private CommandSource source; @Shadow @Final @Mutable private Vec3 worldPosition; @Shadow @Final @Mutable private Vec2 rotation; @Shadow @Final @Mutable private ServerLevel level; - @Shadow @Final @Mutable private int permissionLevel; + @Shadow @Final @Mutable private PermissionSet permissions; @Shadow @Final private Component displayName; @Shadow @Final private String textName; @@ -84,44 +88,61 @@ public abstract class CommandSourceStackMixin implements CommandSourceStackBridg @Shadow @Final private CommandSigningContext signingContext; @Shadow @Final private TaskChainer chatMessageChainer; + //@formatter:on + private Cause impl$cause; @Nullable private Supplier impl$potentialPermissionNode = null; + private @Nullable PermissionService impl$permissionService; - @Inject(method = "(Lnet/minecraft/commands/CommandSource;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/Vec2;Lnet/minecraft/server/level/ServerLevel;ILjava/lang/String;Lnet/minecraft/network/chat/Component;Lnet/minecraft/server/MinecraftServer;Lnet/minecraft/world/entity/Entity;ZLnet/minecraft/commands/CommandResultCallback;Lnet/minecraft/commands/arguments/EntityAnchorArgument$Anchor;Lnet/minecraft/commands/CommandSigningContext;Lnet/minecraft/util/TaskChainer;)V", - at = @At("RETURN")) + @Inject(method = "(Lnet/minecraft/commands/CommandSource;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/Vec2;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/server/permissions/PermissionSet;Ljava/lang/String;Lnet/minecraft/network/chat/Component;Lnet/minecraft/server/MinecraftServer;Lnet/minecraft/world/entity/Entity;ZLnet/minecraft/commands/CommandResultCallback;Lnet/minecraft/commands/arguments/EntityAnchorArgument$Anchor;Lnet/minecraft/commands/CommandSigningContext;Lnet/minecraft/util/TaskChainer;)V", + at = @At("RETURN")) private void impl$setCauseOnConstruction( - final CommandSource $$0, - final Vec3 $$1, - final Vec2 $$2, - final ServerLevel $$3, - final int $$4, - final String $$5, - final Component $$6, - final MinecraftServer $$7, - final Entity $$8, - final boolean $$9, - final CommandResultCallback $$10, - final EntityAnchorArgument.Anchor $$11, - final CommandSigningContext $$12, - final TaskChainer $$13, - final CallbackInfo ci + final CommandSource $$0, + final Vec3 $$1, + final Vec2 $$2, + final ServerLevel $$3, + final PermissionSet permissionLevel, + final String $$5, + final Component $$6, + final MinecraftServer $$7, + final Entity $$8, + final boolean $$9, + final CommandResultCallback $$10, + final EntityAnchorArgument.Anchor $$11, + final CommandSigningContext $$12, + final TaskChainer $$13, + final CallbackInfo ci ) { this.impl$cause = PhaseTracker.getInstance().currentCause(); final EventContext context = this.impl$cause.context(); - context.get(EventContextKeys.LOCATION).ifPresent(x ->{ + context.get(EventContextKeys.LOCATION).ifPresent(x -> { this.worldPosition = VecHelper.toVanillaVector3d(x.position()); this.level = (ServerLevel) x.world(); }); context.get(EventContextKeys.ROTATION).ifPresent(x -> this.rotation = new Vec2((float) x.x(), (float) x.y())); context.get(EventContextKeys.SUBJECT).ifPresent(x -> { - if (x instanceof EntityAccessor) { - this.permissionLevel = ((EntityAccessor) x).invoker$getPermissionLevel(); - } else if (x instanceof MinecraftServer && !((MinecraftServer) x).isSingleplayer()) { - this.permissionLevel = 4; + if (x instanceof PlayerAccessor pa) { + this.permissions = pa.invoker$permissions(); + } else if (x instanceof MinecraftServer ms && !ms.isSingleplayer()) { + this.permissions = PermissionSet.ALL_PERMISSIONS; } }); + this.permissions = (incoming) -> { + if (this.impl$potentialPermissionNode != null) { + final var opLevel = SpongeCommon.server().operatorUserPermissions().level(); + final String perm = this.impl$potentialPermissionNode.get(); + // This will register the permission with the first op level we retrieve. + if (this.impl$permissionService != null) { + SpongePermissions.registerPermission(this.impl$permissionService, perm, opLevel); + } else if (Sponge.isServerAvailable()) { + SpongePermissions.registerPermission(Sponge.server().serviceProvider().permissionService(), perm, opLevel); + } + return ((CommandCause) this).hasPermission(perm); + } + return permissionLevel.hasPermission(incoming); + }; } /* @@ -130,29 +151,32 @@ public abstract class CommandSourceStackMixin implements CommandSourceStackBridg * FIRST. That way, we don't overwrite any changes we then need to make. */ @Inject(method = { - "withEntity", - "withPosition", - "withRotation(Lnet/minecraft/world/phys/Vec2;)Lnet/minecraft/commands/CommandSourceStack;", - "withCallback(Lnet/minecraft/commands/CommandResultCallback;)Lnet/minecraft/commands/CommandSourceStack;", - "withSuppressedOutput", - "withPermission", - "withMaximumPermission", - "withAnchor", - "withLevel" + "withEntity", + "withPosition", + "withRotation(Lnet/minecraft/world/phys/Vec2;)Lnet/minecraft/commands/CommandSourceStack;", + "withCallback(Lnet/minecraft/commands/CommandResultCallback;)Lnet/minecraft/commands/CommandSourceStack;", + "withSuppressedOutput", + "withPermission", + "withMaximumPermission", + "withAnchor", + "withLevel" }, at = @At("RETURN")) private void impl$copyPermissionOnCopy(final CallbackInfoReturnable cir) { if (cir.getReturnValue() != (Object) this) { final CommandSourceStackBridge commandSourceStackBridge = ((CommandSourceStackBridge) cir.getReturnValue()); commandSourceStackBridge.bridge$setPotentialPermissionNode(this.impl$potentialPermissionNode); commandSourceStackBridge.bridge$setCause(this.impl$cause); + commandSourceStackBridge.bridge$permissionService(this.impl$permissionService); } } @Override public CommandCause bridge$withCurrentCause() { // Cause is set in ctor. - return (CommandCause) CommandSourceStackAccessor.invoker$new(this.source, this.worldPosition, this.rotation, this.level, this.permissionLevel, - this.textName, this.displayName, this.server, this.entity, this.silent, this.resultCallback, this.anchor, this.signingContext, this.chatMessageChainer); + final CommandCause instance = (CommandCause) CommandSourceStackAccessor.invoker$new(this.source, this.worldPosition, this.rotation, this.level, this.permissions, + this.textName, this.displayName, this.server, this.entity, this.silent, this.resultCallback, this.anchor, this.signingContext, this.chatMessageChainer); + ((CommandSourceStackBridge) instance).bridge$permissionService(this.impl$permissionService); + return instance; } /* @@ -169,9 +193,9 @@ public abstract class CommandSourceStackMixin implements CommandSourceStackBridg private void impl$updateCauseOnWithWorld(final ServerLevel serverWorld, final CallbackInfoReturnable cir) { if (cir.getReturnValue() != (Object) this) { final ServerLocation location = this.impl$cause.context().get(EventContextKeys.LOCATION) - .map(x -> ServerLocation.of((org.spongepowered.api.world.server.ServerWorld) serverWorld, x.position())) - .orElseGet(() -> ServerLocation.of((org.spongepowered.api.world.server.ServerWorld) serverWorld, - VecHelper.toVector3d(cir.getReturnValue().getPosition()))); + .map(x -> ServerLocation.of((org.spongepowered.api.world.server.ServerWorld) serverWorld, x.position())) + .orElseGet(() -> ServerLocation.of((org.spongepowered.api.world.server.ServerWorld) serverWorld, + VecHelper.toVector3d(cir.getReturnValue().getPosition()))); ((CommandSourceStackBridge) cir.getReturnValue()).bridge$setCause(this.impl$applyToCause(EventContextKeys.LOCATION, location)); } } @@ -181,8 +205,8 @@ public abstract class CommandSourceStackMixin implements CommandSourceStackBridg if (cir.getReturnValue() != (Object) this) { final org.spongepowered.math.vector.Vector3d position = VecHelper.toVector3d(pos); final ServerLocation location = this.impl$cause.context().get(EventContextKeys.LOCATION) - .map(x -> ServerLocation.of(x.world(), position)) - .orElseGet(() -> ServerLocation.of((org.spongepowered.api.world.server.ServerWorld) cir.getReturnValue().getLevel(), position)); + .map(x -> ServerLocation.of(x.world(), position)) + .orElseGet(() -> ServerLocation.of((org.spongepowered.api.world.server.ServerWorld) cir.getReturnValue().getLevel(), position)); ((CommandSourceStackBridge) cir.getReturnValue()).bridge$setCause(this.impl$applyToCause(EventContextKeys.LOCATION, location)); } } @@ -195,17 +219,6 @@ public abstract class CommandSourceStackMixin implements CommandSourceStackBridg } } - @Inject(method = "hasPermission", at = @At(value = "HEAD"), cancellable = true) - private void impl$checkPermission(final int opLevel, final CallbackInfoReturnable cir) { - if (Sponge.isServerAvailable() && this.impl$potentialPermissionNode != null) { - final String perm = this.impl$potentialPermissionNode.get(); - // This will register the permission with the first op level we retrieve. - SpongePermissions.registerPermission(perm, opLevel); - cir.setReturnValue(((CommandCause) this).hasPermission(perm)); - } - // fall through to the op level check if we haven't set a permission node. - } - @Override public void bridge$setPotentialPermissionNode(final @Nullable Supplier permission) { this.impl$potentialPermissionNode = permission; @@ -250,4 +263,8 @@ public abstract class CommandSourceStackMixin implements CommandSourceStackBridg return Cause.builder().from(this.impl$cause).build(builder.build()); } + @Override + public void bridge$permissionService(final PermissionService permissionService) { + this.impl$permissionService = permissionService; + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/commands/CommandsMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/commands/CommandsMixin.java index 2feb6e6fc04..6af2d4d8b82 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/commands/CommandsMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/commands/CommandsMixin.java @@ -25,79 +25,46 @@ package org.spongepowered.common.mixin.core.commands; import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.builder.RequiredArgumentBuilder; -import com.mojang.brigadier.suggestion.SuggestionProvider; -import com.mojang.brigadier.tree.ArgumentCommandNode; -import com.mojang.brigadier.tree.CommandNode; -import com.mojang.brigadier.tree.RootCommandNode; +import net.minecraft.commands.CommandBuildContext; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.commands.synchronization.SuggestionProviders; import net.minecraft.server.commands.AdvancementCommands; -import net.minecraft.server.level.ServerPlayer; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.common.bridge.commands.CommandsBridge; -import org.spongepowered.common.bridge.commands.arguments.CompletionsArgumentTypeBridge; import org.spongepowered.common.command.brigadier.dispatcher.DelegatingCommandDispatcher; -import org.spongepowered.common.command.brigadier.dispatcher.SpongeNodePermissionCache; -import org.spongepowered.common.command.brigadier.tree.SpongeArgumentCommandNode; -import org.spongepowered.common.command.brigadier.tree.SpongeNode; -import org.spongepowered.common.command.brigadier.tree.SuggestionArgumentNode; import org.spongepowered.common.command.manager.SpongeCommandManager; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.launch.Launch; -import org.spongepowered.common.util.CommandUtil; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; @Mixin(Commands.class) public abstract class CommandsMixin implements CommandsBridge { - // @formatter:off - @Shadow private void shadow$fillUsableCommands(final CommandNode rootCommandSource, - final CommandNode rootSuggestion, - final CommandSourceStack source, - final Map, CommandNode> commandNodeToSuggestionNode) { - throw new AssertionError("This shouldn't be callable"); - } - // @formatter:on - private CauseStackManager.@MonotonicNonNull StackFrame impl$initFrame = null; - private final WeakHashMap, List>>> impl$playerNodeCache = - new WeakHashMap<>(); private @MonotonicNonNull SpongeCommandManager impl$commandManager; // We prepare our own dispatcher and commands manager, to redirect registrations to our system @Redirect(method = "", at = @At( - value = "NEW", - args = "class=com/mojang/brigadier/CommandDispatcher", - remap = false + value = "NEW", + args = "class=com/mojang/brigadier/CommandDispatcher", + remap = false )) - private CommandDispatcher impl$useSpongeDispatcher() { + private CommandDispatcher impl$useSpongeDispatcher(final Commands.CommandSelection selection, final CommandBuildContext context) { if (!Launch.instance().pluginManager().isReady()) { return new CommandDispatcher<>(); } final SpongeCommandManager manager = Launch.instance().lifecycle().platformInjector().getInstance(SpongeCommandManager.class); - manager.init(); + manager.init((RegistryHolder) context); this.impl$commandManager = manager; return new DelegatingCommandDispatcher(manager.getBrigadierRegistrar()); } @Redirect(method = "", at = @At(value = "INVOKE", - target = "Lnet/minecraft/server/commands/AdvancementCommands;register(Lcom/mojang/brigadier/CommandDispatcher;)V")) + target = "Lnet/minecraft/server/commands/AdvancementCommands;register(Lcom/mojang/brigadier/CommandDispatcher;)V")) private void impl$setupStackFrameOnInit(final CommandDispatcher dispatcher) { if (Launch.instance().pluginManager().isReady()) { this.impl$initFrame = PhaseTracker.getInstance().pushCauseFrame(); @@ -114,210 +81,8 @@ public abstract class CommandsMixin implements CommandsBridge { } } - /* - * Hides nodes that we have marked as "hidden" - */ - @Redirect(method = "fillUsableCommands", - slice = @Slice( - from = @At("HEAD"), - to = @At(value = "INVOKE", remap = false, target = "Ljava/util/Iterator;hasNext()Z") - ), - at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/tree/CommandNode;getChildren()Ljava/util/Collection;", remap = false)) - private Collection> impl$handleHiddenChildrenAndEnsureUnsortedLoop(final CommandNode commandNode) { - return this.impl$getChildrenFromNode(commandNode); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Redirect(method = "fillUsableCommands", - at = @At(value = "INVOKE", - target = "Lcom/mojang/brigadier/tree/CommandNode;createBuilder()Lcom/mojang/brigadier/builder/ArgumentBuilder;", - remap = false)) - private ArgumentBuilder impl$createArgumentBuilder( - final CommandNode commandNode, - final CommandNode rootCommandSource, - final CommandNode rootSuggestion, - final CommandSourceStack source, - final Map, CommandNode> commandNodeToSuggestionNode) { - if (commandNode instanceof SpongeArgumentCommandNode node) { - return node.createBuilderForSuggestions(rootSuggestion, commandNodeToSuggestionNode); - } - - if (commandNode instanceof ArgumentCommandNode acn && acn.getType() instanceof CompletionsArgumentTypeBridge catb) { - final RequiredArgumentBuilder builder = RequiredArgumentBuilder.argument(acn.getName(), catb.bridge$clientSideCompletionType()); - builder.executes(acn.getCommand()) - .forward(acn.getRedirect(), acn.getRedirectModifier(), acn.isFork()) - .requires(acn.getRequirement()); - if (!CommandUtil.checkForCustomSuggestions(rootSuggestion)) { - builder.suggests((SuggestionProvider) SuggestionProviders.ASK_SERVER); - } - return builder; - } - return commandNode.createBuilder(); - } - - @SuppressWarnings("unchecked") - @Redirect(method = "fillUsableCommands", at = - @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", remap = false)) - private V impl$preventPutIntoMapIfNodeIsComplex(final Map map, - final K key, - final V value, - final CommandNode rootCommandSource, - final CommandNode rootSuggestion, - final CommandSourceStack source, - final Map, CommandNode> commandNodeToSuggestionNode) { - if (!map.containsKey(key)) { - // done here because this check is applicable - final ServerPlayer e = (ServerPlayer) source.getEntity(); - final Map, List>> playerNodes = this.impl$playerNodeCache.get(e); - if (!playerNodes.containsKey(key)) { - final List> children = new ArrayList<>(); - children.add((CommandNode) value); - playerNodes.put((CommandNode) key, children); - } - - // If the current root suggestion has already got a custom suggestion and this node has a custom suggestion, - // we need to swap it out. - if (value instanceof ArgumentCommandNode && CommandUtil.checkForCustomSuggestions(rootSuggestion)) { - rootSuggestion.addChild(this.impl$cloneArgumentCommandNodeWithoutSuggestions((ArgumentCommandNode) value)); - } else { - rootSuggestion.addChild((CommandNode) value); - } - return map.put(key, value); - } - return null; // it's ignored anyway. - } - - @Redirect(method = "fillUsableCommands", - at = @At(value = "INVOKE", - target = "Lcom/mojang/brigadier/tree/CommandNode;addChild(Lcom/mojang/brigadier/tree/CommandNode;)V", - remap = false)) - private void impl$preventAddChild(final CommandNode root, final CommandNode newChild) { - // no-op, we did this above. - } - - @Redirect(method = "fillUsableCommands", - at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/tree/CommandNode;canUse(Ljava/lang/Object;)Z", remap = false)) - private boolean impl$testPermissionAndPreventRecalculationWhenSendingNodes( - final CommandNode commandNode, - final Object source, - final CommandNode rootCommandNode, - final CommandNode rootSuggestion, - final CommandSourceStack sourceButTyped, - final Map, CommandNode> commandNodeToSuggestionNode - ) { - final ServerPlayer e = (ServerPlayer) sourceButTyped.getEntity(); - final Map, List>> playerNodes = this.impl$playerNodeCache.get(e); - final List> existingNodes = playerNodes.get(commandNode); - if (existingNodes != null) { - if (!existingNodes.isEmpty()) { - boolean hasCustomSuggestionsAlready = CommandUtil.checkForCustomSuggestions(rootSuggestion); - for (final CommandNode node : existingNodes) { - // If we have custom suggestions, we need to limit it to one node, otherwise we trigger a bug - // in the client where it'll send more than one custom suggestion request - which is fine, except - // the client will then ignore all but one of them. This is a problem because we then end up with - // no suggestions - CompletableFuture.allOf(...) will contain an exception if a future is cancelled, - // meaning thenRun(...) does not run, which is how displaying the suggestions works... - // - // Because we don't control the client, we have to work around it here. - if (hasCustomSuggestionsAlready && node instanceof ArgumentCommandNode) { - final ArgumentCommandNode argNode = (ArgumentCommandNode) node; - if (argNode.getCustomSuggestions() != null) { - // Rebuild the node without the custom suggestions. - rootSuggestion.addChild(this.impl$cloneArgumentCommandNodeWithoutSuggestions(argNode)); - continue; - } - } else if (node instanceof ArgumentCommandNode && ((ArgumentCommandNode) node).getCustomSuggestions() != null) { - hasCustomSuggestionsAlready = true; // no more custom suggestions - } - rootSuggestion.addChild(node); - } - } - // If empty, we have a node won't resolve (even if not complex), so we ignore it. - return false; - // If we have already processed this node and it appears in the suggestion node list, prevent a potentially costly - // canUse check as we know we can already use it. - } else if (!commandNodeToSuggestionNode.containsKey(commandNode) && !SpongeNodePermissionCache.canUse( - rootCommandNode instanceof RootCommandNode, this.impl$commandManager.getDispatcher(), commandNode, sourceButTyped)) { - playerNodes.put(commandNode, Collections.emptyList()); - return false; - } - - if (commandNode instanceof SpongeArgumentCommandNode && ((SpongeArgumentCommandNode) commandNode).isComplex()) { - final boolean hasCustomSuggestionsAlready = CommandUtil.checkForCustomSuggestions(rootSuggestion); - final CommandNode finalCommandNode = ((SpongeArgumentCommandNode) commandNode).getComplexSuggestions( - rootSuggestion, - commandNodeToSuggestionNode, - playerNodes, - !hasCustomSuggestionsAlready - ); - if (!this.impl$getChildrenFromNode(commandNode).isEmpty()) { - this.shadow$fillUsableCommands(commandNode, finalCommandNode, sourceButTyped, commandNodeToSuggestionNode); - } - return false; - } - - // Normal node, handle it normally. We don't add to the playerNodeCache - the commandNodeToSuggestionNode map handles this. - return true; - } - - @Redirect(method = "fillUsableCommands", at = @At(value = "INVOKE", - target = "Lcom/mojang/brigadier/builder/RequiredArgumentBuilder;suggests(Lcom/mojang/brigadier/suggestion/SuggestionProvider;)Lcom/mojang/brigadier/builder/RequiredArgumentBuilder;", remap = false)) - private RequiredArgumentBuilder impl$dontAskServerIfSiblingAlreadyDoes( - final RequiredArgumentBuilder requiredArgumentBuilder, - final SuggestionProvider provider, - final CommandNode rootCommandNode, - final CommandNode rootSuggestion, - final CommandSourceStack sourceButTyped, - final Map, CommandNode> commandNodeToSuggestionNode - ) { - // From above. - // - // If we have custom suggestions, we need to limit it to one node, otherwise we trigger a bug - // in the client where it'll send more than one custom suggestion request - which is fine, except - // the client will then ignore all but one of them. This is a problem because we then end up with - // no suggestions - CompletableFuture.allOf(...) will contain an exception if a future is cancelled, - // meaning thenRun(...) does not run, which is how displaying the suggestions works... - // - // Because we don't control the client, we have to work around it here. - if (provider != SuggestionProviders.ASK_SERVER || !CommandUtil.checkForCustomSuggestions(rootSuggestion)) { - requiredArgumentBuilder.suggests(provider); - } - return requiredArgumentBuilder; - } - - - @SuppressWarnings("unchecked") - @Redirect(method = "fillUsableCommands", - at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/builder/ArgumentBuilder;build()Lcom/mojang/brigadier/tree/CommandNode;", remap = false)) - private CommandNode impl$createSpongeArgumentNode(final ArgumentBuilder argumentBuilder) { - if (argumentBuilder instanceof RequiredArgumentBuilder) { - return new SuggestionArgumentNode<>((RequiredArgumentBuilder) argumentBuilder); - } - return argumentBuilder.build(); - } - @Override public SpongeCommandManager bridge$commandManager() { return this.impl$commandManager; } - - private Collection> impl$getChildrenFromNode(final CommandNode parentNode) { - final Collection> nodes; - if (parentNode instanceof SpongeNode) { - nodes = ((SpongeNode) parentNode).getChildrenForSuggestions(); - } else { - nodes = parentNode.getChildren(); - } - return nodes; - } - - private ArgumentCommandNode impl$cloneArgumentCommandNodeWithoutSuggestions(final ArgumentCommandNode toClone) { - final RequiredArgumentBuilder builder = toClone.createBuilder(); - builder.suggests(null); - for (final CommandNode node : toClone.getChildren()) { - builder.then(node); - } - return new SuggestionArgumentNode<>(builder); - } - } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/commands/arguments/DimensionArgumentMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/commands/arguments/DimensionArgumentMixin.java index 73adc701a2b..a4dd0601e21 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/commands/arguments/DimensionArgumentMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/commands/arguments/DimensionArgumentMixin.java @@ -26,17 +26,17 @@ import com.mojang.brigadier.arguments.ArgumentType; import net.minecraft.commands.arguments.DimensionArgument; -import net.minecraft.commands.arguments.ResourceLocationArgument; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.commands.arguments.IdentifierArgument; +import net.minecraft.resources.Identifier; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.bridge.commands.arguments.CompletionsArgumentTypeBridge; @Mixin(DimensionArgument.class) -public abstract class DimensionArgumentMixin implements CompletionsArgumentTypeBridge { +public abstract class DimensionArgumentMixin implements CompletionsArgumentTypeBridge { @Override - public ArgumentType bridge$clientSideCompletionType() { - return ResourceLocationArgument.id(); + public ArgumentType bridge$clientSideCompletionType() { + return IdentifierArgument.id(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/commands/arguments/selector/EntitySelectorParserMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/commands/arguments/selector/EntitySelectorParserMixin.java index 1f2aeff13da..24bef2ecac7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/commands/arguments/selector/EntitySelectorParserMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/commands/arguments/selector/EntitySelectorParserMixin.java @@ -105,7 +105,7 @@ public abstract class EntitySelectorParserMixin implements EntitySelectorParserB return this.reader; } - @Inject(method = "allowSelectors", at = @At("HEAD"), cancellable = true) + @Inject(method = "allowSelectors(Ljava/lang/Object;)Z", at = @At("HEAD"), cancellable = true) private static void impl$onAllowSelectors(final S $$0, final CallbackInfoReturnable cir) { if ($$0 instanceof final CommandCause cause && cause.hasPermission(Constants.Permissions.SELECTOR_PERMISSION)) { cir.setReturnValue(true); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/core/MappedRegistryMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/core/MappedRegistryMixin.java index 61d5d664916..112a355627f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/core/MappedRegistryMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/core/MappedRegistryMixin.java @@ -31,10 +31,14 @@ import net.minecraft.core.MappedRegistry; import net.minecraft.core.RegistrationInfo; import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.objectweb.asm.Opcodes; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.registry.RegistryEntry; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.api.registry.ValueNotFoundException; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -42,32 +46,52 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.accessor.core.MappedRegistryAccessor; import org.spongepowered.common.accessor.resources.ResourceKeyAccessor; import org.spongepowered.common.bridge.core.MappedRegistryBridge; import org.spongepowered.common.bridge.core.RegistryBridge; import org.spongepowered.common.bridge.core.WritableRegistryBridge; +import org.spongepowered.common.registry.InitialRegistryData; import org.spongepowered.common.registry.SpongeRegistryEntry; import org.spongepowered.common.registry.SpongeRegistryType; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Stream; @Mixin(MappedRegistry.class) public abstract class MappedRegistryMixin implements RegistryBridge, WritableRegistryBridge, MappedRegistryBridge { + // @formatter:off + @Shadow private boolean frozen; @Shadow @Final private ObjectList> byId; @Shadow @Final private Reference2IntMap toId; - @Shadow @Final private Map> byLocation; + @Shadow @Final private Map> byLocation; @Shadow @Final private Map, Holder.Reference> byKey; @Shadow @Final private Map> byValue; @Shadow @Final private Map, RegistrationInfo> registrationInfos; + @Shadow @Final private net.minecraft.resources.ResourceKey> key; + @Shadow MappedRegistry.TagSet allTags; + @Shadow public abstract Holder.Reference shadow$register(net.minecraft.resources.ResourceKey arg, T object, RegistrationInfo arg2); + // @formatter:on + + private RegistryHolder impl$registryHolder; private RegistryType impl$type; private final Map> impl$entries = new LinkedHashMap<>(); + private final Set> impl$dependencies = new HashSet<>(); + private final List impl$preFreezeTasks = new ArrayList<>(); + private boolean impl$isDynamic = true; + private boolean impl$eventCalled = false; @Override public boolean bridge$isDynamic() { @@ -84,7 +108,7 @@ public abstract class MappedRegistryMixin implements RegistryBridge, Writa private void impl$setType(final net.minecraft.resources.ResourceKey> key, final Lifecycle lifecycle, boolean $$2, final CallbackInfo ci) { this.impl$type = new SpongeRegistryType((ResourceKey) (Object) ((ResourceKeyAccessor) key).accessor$registryName(), - (ResourceKey) (Object) key.location()); + (ResourceKey) (Object) key.identifier()); } @@ -94,9 +118,9 @@ public abstract class MappedRegistryMixin implements RegistryBridge, Writa final net.minecraft.resources.ResourceKey> resourceKey = ((MappedRegistry) (Object) this).key(); final ResourceKey root = (ResourceKey) (Object) ((ResourceKeyAccessor) resourceKey).accessor$registryName(); - final ResourceKey location = (ResourceKey) (Object) resourceKey.location(); + final ResourceKey location = (ResourceKey) (Object) resourceKey.identifier(); this.bridge$register(new SpongeRegistryEntry<>(new SpongeRegistryType<>(root, location), - (ResourceKey) (Object) $$0.location(), $$1)); + (ResourceKey) (Object) $$0.identifier(), $$1)); } @Override @@ -114,6 +138,11 @@ public abstract class MappedRegistryMixin implements RegistryBridge, Writa return Optional.ofNullable(this.impl$entries.get(resourceKey)); } + @Override + public @Nullable RegistryEntry bridge$getEntry(final ResourceKey resourceKey) { + return this.impl$entries.get(resourceKey); + } + @Override public Stream> bridge$streamEntries() { return this.impl$entries.values().stream(); @@ -128,9 +157,74 @@ public abstract class MappedRegistryMixin implements RegistryBridge, Writa for (int i = id; i < this.byId.size(); i++) { this.toId.put(this.byId.get(i).value(), i); } - this.byLocation.remove(key.location()); + this.byLocation.remove(key.identifier()); this.byValue.remove(value.value()); this.registrationInfos.remove(key); - this.impl$entries.remove((ResourceKey) (Object) key.location()); + this.impl$entries.remove((ResourceKey) (Object) key.identifier()); + } + + @Inject(method = "freeze", at = @At(value = "FIELD", target = "Lnet/minecraft/core/MappedRegistry;frozen:Z", opcode = Opcodes.PUTFIELD)) + private void impl$onFreeze(final CallbackInfoReturnable> cir) { + this.impl$dependencies.forEach(t -> { + if (!this.impl$registryHolder.findRegistry(t) + .map(r -> ((MappedRegistryAccessor) r).accessor$frozen()) + .orElse(false)) { + throw new ValueNotFoundException(String.format("Dependency %s was not found!", t)); + } + }); + this.impl$preFreezeTasks.forEach(Runnable::run); + this.impl$preFreezeTasks.clear(); + } + + @Override + public void bridge$setRegistryHolder(final RegistryHolder registryHolder) { + this.impl$registryHolder = registryHolder; + } + + @Override + public void bridge$addDependencies(final Supplier> supplier, final RegistryType... dependencies) { + if (Arrays.stream(dependencies).allMatch(d -> this.impl$registryHolder.findRegistry(d) + .map(r -> ((MappedRegistryAccessor) r).accessor$frozen()) + .orElse(false))) { + this.impl$appendRegister(supplier); + return; + } + this.bridge$addDependencies(() -> this.impl$appendRegister(supplier), dependencies); + } + + private void impl$appendRegister(final Supplier> supplier) { + supplier.get().forEach((vk, vi, vv) -> + this.shadow$register( + net.minecraft.resources.ResourceKey.create(this.key, (Identifier) (Object) vk), + vv, + RegistrationInfo.BUILT_IN + )); + } + + @Override + public void bridge$addDependencies(final Runnable runnable, final RegistryType... dependencies) { + this.impl$dependencies.addAll(List.of(dependencies)); + this.impl$preFreezeTasks.add(runnable); + } + + @Override + public Stream> bridge$pendingDependencies() { + return this.impl$dependencies.stream(); + } + + @Override + public void bridge$markEventCalled() { + this.impl$eventCalled = true; + } + + @Override + public boolean bridge$eventCalled() { + return this.impl$eventCalled; + } + + @Override + public void bridge$unfreeze() { + this.frozen = false; + this.allTags = MappedRegistry.TagSet.unbound(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/core/dispenser/ProjectileDispenseBehaviorMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/core/dispenser/ProjectileDispenseBehaviorMixin.java index 0ef52cdaf02..410158c665e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/core/dispenser/ProjectileDispenseBehaviorMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/core/dispenser/ProjectileDispenseBehaviorMixin.java @@ -24,11 +24,13 @@ */ package org.spongepowered.common.mixin.core.core.dispenser; +import net.minecraft.core.Direction; +import net.minecraft.core.Position; import net.minecraft.core.dispenser.BlockSource; import net.minecraft.core.dispenser.DefaultDispenseItemBehavior; import net.minecraft.core.dispenser.ProjectileDispenseBehavior; -import net.minecraft.world.entity.Entity; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ProjectileItem; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import org.spongepowered.api.data.Keys; @@ -42,13 +44,17 @@ public abstract class ProjectileDispenseBehaviorMixin extends DefaultDispenseItemBehavior { @Redirect(method = "execute", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z")) - private boolean impl$spawnEntityAndSetShooter(final Level world, final Entity entity, final BlockSource source, final ItemStack stack) { + target = "Lnet/minecraft/world/item/ProjectileItem;asProjectile(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/Position;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/core/Direction;)Lnet/minecraft/world/entity/projectile/Projectile;")) + private net.minecraft.world.entity.projectile.Projectile impl$spawnEntityAndSetShooter(final ProjectileItem instance, final Level level, + final Position position, final ItemStack stack, final Direction direction, final BlockSource source) { + final var entity = instance.asProjectile(level, position, stack, direction); + final BlockEntity tileEntity = source.blockEntity(); if (entity instanceof Projectile && tileEntity instanceof ProjectileSource) { ((Projectile) entity).offer(Keys.SHOOTER, (ProjectileSource) tileEntity); } - return world.addFreshEntity(entity); + + return entity; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/item/EmptyMapItemMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/item/EmptyMapItemMixin.java index ad4d46a8af3..9663eb5f067 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/item/EmptyMapItemMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/item/EmptyMapItemMixin.java @@ -29,8 +29,9 @@ import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalIntRef; import net.minecraft.core.component.DataComponents; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.InteractionResult; import net.minecraft.world.item.EmptyMapItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -63,7 +64,7 @@ public abstract class EmptyMapItemMixin { @Inject(method = "use", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;consume(ILnet/minecraft/world/entity/LivingEntity;)V"), cancellable = true) private void impl$prepareMap(final Level level, final net.minecraft.world.entity.player.Player player, - final InteractionHand usedHand, final CallbackInfoReturnable> cir, + final InteractionHand usedHand, final CallbackInfoReturnable cir, @Local final ItemStack usedItem, @Share("id") final LocalIntRef mapId) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.addContext(EventContextKeys.PLAYER, (Player) player); @@ -77,7 +78,7 @@ public abstract class EmptyMapItemMixin { final Optional mapInfo = SpongeCommonEventFactory.fireCreateMapEvent(frame.currentCause(), mapValues); if (mapInfo.isEmpty()) { - cir.setReturnValue(InteractionResultHolder.fail(usedItem)); + cir.setReturnValue(InteractionResult.FAIL); return; } @@ -85,8 +86,8 @@ public abstract class EmptyMapItemMixin { } } - @Redirect(method = "use", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/MapItem;create(Lnet/minecraft/world/level/Level;IIBZZ)Lnet/minecraft/world/item/ItemStack;")) - private ItemStack impl$createMap(final Level level, final int blockX, final int blockZ, + @Redirect(method = "use", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/MapItem;create(Lnet/minecraft/server/level/ServerLevel;IIBZZ)Lnet/minecraft/world/item/ItemStack;")) + private ItemStack impl$createMap(final ServerLevel level, final int blockX, final int blockZ, final byte scale, final boolean trackingPosition, final boolean unlimitedTracking, @Share("id") final LocalIntRef mapId) { final ItemStack newMap = new ItemStack(Items.FILLED_MAP, 1); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/nbt/CompoundTagMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/nbt/CompoundTagMixin.java index 0e4a4191bb2..316418741e4 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/nbt/CompoundTagMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/nbt/CompoundTagMixin.java @@ -24,7 +24,6 @@ */ package org.spongepowered.common.mixin.core.nbt; -import com.google.common.base.Function; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import org.apache.logging.log4j.Level; @@ -37,6 +36,7 @@ import org.spongepowered.common.util.PrettyPrinter; import java.util.Map; +import java.util.function.BiConsumer; @Mixin(CompoundTag.class) public abstract class CompoundTagMixin { @@ -45,11 +45,11 @@ public abstract class CompoundTagMixin { @Shadow @Final private Map tags; // @formatter:on - @ModifyArg(method = "copy()Lnet/minecraft/nbt/CompoundTag;", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Maps;transformValues(Ljava/util/Map;Lcom/google/common/base/Function;)Ljava/util/Map;")) - private Function impl$checkForOverflowOnCopy(final Function copy) { - return (tag) -> { + @ModifyArg(method = "copy()Lnet/minecraft/nbt/CompoundTag;", at = @At(value = "INVOKE", target = "Ljava/util/Map;forEach(Ljava/util/function/BiConsumer;)V"), require = 0) + private BiConsumer impl$checkForOverflowOnCopy(final BiConsumer putCopy) { + return (key, tag) -> { try { - return copy.apply(tag); + putCopy.accept(key, tag); } catch (final StackOverflowError e) { final PrettyPrinter printer = new PrettyPrinter(60) .add("StackOverflow from trying to copy this compound") diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/network/ConnectionMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/network/ConnectionMixin.java index fbb419193ce..5ad56e567f6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/network/ConnectionMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/network/ConnectionMixin.java @@ -27,12 +27,12 @@ import com.google.common.collect.Sets; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.local.LocalAddress; import net.minecraft.network.Connection; import net.minecraft.network.PacketListener; -import net.minecraft.network.PacketSendListener; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.PacketFlow; @@ -163,10 +163,11 @@ public abstract class ConnectionMixin extends SimpleChannelInboundHandler $$0, final @Nullable PacketSendListener $$1, final boolean $$2) { + private boolean impl$onQueue(final Queue> instance, final E consumer, + final Packet $$0, final @Nullable ChannelFutureListener $$1, final boolean $$2) { if (this.impl$disconnected) { if ($$1 instanceof final PacketSender.SpongePacketSendListener spongeListener) { spongeListener.accept(new IOException("Connection has been closed.")); @@ -188,7 +189,7 @@ public void accept(final Connection connection) { } }); } else { - result = instance.add(consumer); + result = instance.add((Consumer) consumer); } if (this.impl$disconnected) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/network/PacketEncoderMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/network/PacketEncoderMixin.java index 9e6c9c09ca8..9dae20a9a44 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/network/PacketEncoderMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/network/PacketEncoderMixin.java @@ -26,9 +26,12 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; +import net.minecraft.network.Connection; import net.minecraft.network.PacketEncoder; import net.minecraft.network.PacketListener; import net.minecraft.network.protocol.Packet; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -42,6 +45,9 @@ public class PacketEncoderMixin { at = @At(value = "INVOKE", target = "Lnet/minecraft/network/codec/StreamCodec;encode(Ljava/lang/Object;Ljava/lang/Object;)V")) private void impl$setLocaleBeforeEncode(final ChannelHandlerContext ctx, final Packet $$1, final ByteBuf $$2, final CallbackInfo ci) { SpongeAdventure.ENCODING_LOCALE.set(ctx.channel().attr(SpongeAdventure.CHANNEL_LOCALE).get()); + if (ctx.pipeline().get(Connection.class).getPacketListener() instanceof final ServerGamePacketListenerImpl serverGamePacketListener) { + SpongeAdventure.ENCODING_PLAYER.set((ServerPlayer) serverGamePacketListener.player); + } } @Inject(method = "encode(Lio/netty/channel/ChannelHandlerContext;Lnet/minecraft/network/protocol/Packet;Lio/netty/buffer/ByteBuf;)V", @@ -49,5 +55,6 @@ public class PacketEncoderMixin { shift = At.Shift.AFTER)) private void impl$clearLocaleAfterEncode(final ChannelHandlerContext ctx, final Packet $$1, final ByteBuf $$2, final CallbackInfo ci) { SpongeAdventure.ENCODING_LOCALE.remove(); + SpongeAdventure.ENCODING_PLAYER.remove(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/network/chat/ComponentSerializationMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/network/chat/ComponentSerializationMixin.java index a21715d0188..77478a76612 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/network/chat/ComponentSerializationMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/network/chat/ComponentSerializationMixin.java @@ -26,6 +26,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.util.locale.Locales; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -38,10 +39,11 @@ @Mixin(ComponentSerialization.class) public abstract class ComponentSerializationMixin { - @ModifyVariable(method = "lambda$createCodec$6", at = @At(value = "HEAD"), argsOnly = true) + @ModifyVariable(method = "lambda$createCodec$3", at = @At(value = "HEAD"), argsOnly = true) private static Component impl$localizeComponent(final Component input) { final Locale locale = SpongeAdventure.ENCODING_LOCALE.get(); - return NativeComponentRenderer.apply(input, locale == null ? Locales.DEFAULT : locale); + final ServerPlayer player = SpongeAdventure.ENCODING_PLAYER.get(); + return NativeComponentRenderer.apply(input, locale == null ? Locales.DEFAULT : locale, player); } //TODO: Do this for ItemStacks too diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/network/chat/Component_SerializerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/network/chat/Component_SerializerMixin.java index e7fa9067481..97824574f4a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/network/chat/Component_SerializerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/network/chat/Component_SerializerMixin.java @@ -25,19 +25,20 @@ package org.spongepowered.common.mixin.core.network.chat; import com.google.gson.GsonBuilder; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -@Mixin(net.minecraft.network.chat.Component.Serializer.class) +@Mixin(targets = "net/minecraft/network/codec/ByteBufCodecs$34") public abstract class Component_SerializerMixin { // inject into the anonymous function to build a gson instance - @Redirect(method = "", at = @At(value = "INVOKE", target = "com/google/gson/GsonBuilder.disableHtmlEscaping()Lcom/google/gson/GsonBuilder;", remap = false), remap = false) - private static GsonBuilder impl$injectAdventureGson(final GsonBuilder gson) { - gson.disableHtmlEscaping(); - GsonComponentSerializer.gson().populator().apply(gson); - return gson; + @WrapOperation(method = "", at = @At(value = "INVOKE", target = "com/google/gson/GsonBuilder.disableHtmlEscaping()Lcom/google/gson/GsonBuilder;", remap = false), remap = false) + private static GsonBuilder impl$injectAdventureGson(GsonBuilder instance, Operation original) { + original.call(instance); + GsonComponentSerializer.gson().populator().apply(instance); + return instance; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/network/protocol/login/ClientboundCustomQueryPacketMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/network/protocol/login/ClientboundCustomQueryPacketMixin.java index 2c3111d5464..874d59b41eb 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/network/protocol/login/ClientboundCustomQueryPacketMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/network/protocol/login/ClientboundCustomQueryPacketMixin.java @@ -27,7 +27,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; import net.minecraft.network.protocol.login.custom.CustomQueryPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -44,7 +44,7 @@ public abstract class ClientboundCustomQueryPacketMixin { // @formatter: on @Inject(method = "readPayload", at = @At("HEAD"), cancellable = true) - private static void impl$onReadUnknownPayload(final ResourceLocation $$0, final FriendlyByteBuf $$1, final CallbackInfoReturnable cir) { + private static void impl$onReadUnknownPayload(final Identifier $$0, final FriendlyByteBuf $$1, final CallbackInfoReturnable cir) { final int readableBytes = $$1.readableBytes(); if (readableBytes >= 0 && readableBytes <= ClientboundCustomQueryPacketMixin.MAX_PAYLOAD_SIZE) { final var payload = $$1.readBytes(readableBytes); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/network/syncher/SynchedEntityDataMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/network/syncher/SynchedEntityDataMixin.java index e2175da5295..e135c13840b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/network/syncher/SynchedEntityDataMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/network/syncher/SynchedEntityDataMixin.java @@ -84,7 +84,7 @@ public abstract class SynchedEntityDataMixin { final var thisEntity = (Entity) this.entity; - if (thisEntity.level() == null || thisEntity.level().isClientSide || ((EntityBridge) this.entity).bridge$isConstructing()) { + if (thisEntity.level() == null || thisEntity.level().isClientSide() || ((EntityBridge) this.entity).bridge$isConstructing()) { return dataentry; } final Optional> converter = ((EntityDataAccessorBridge) (Object) accessor).bridge$getDataConverter(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/resources/RegistryDataLoaderMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/resources/RegistryLoadTaskMixin.java similarity index 59% rename from src/mixins/java/org/spongepowered/common/mixin/core/resources/RegistryDataLoaderMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/resources/RegistryLoadTaskMixin.java index dec6168190e..b673a771ccf 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/resources/RegistryDataLoaderMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/resources/RegistryLoadTaskMixin.java @@ -24,42 +24,41 @@ */ package org.spongepowered.common.mixin.core.resources; -import com.mojang.serialization.Decoder; import net.minecraft.core.RegistrationInfo; -import net.minecraft.core.WritableRegistry; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.ChatType; import net.minecraft.network.chat.ChatTypeDecoration; -import net.minecraft.resources.RegistryDataLoader; -import net.minecraft.resources.RegistryOps; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.NetworkRegistryLoadTask; +import net.minecraft.resources.RegistryLoadTask; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.ResourceManager; import org.spongepowered.api.adventure.ChatTypes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.accessor.resources.RegistryLoadTaskAccessor; import java.util.Map; -@Mixin(RegistryDataLoader.class) -public class RegistryDataLoaderMixin { +@Mixin(RegistryLoadTask.class) +public class RegistryLoadTaskMixin { @SuppressWarnings("unchecked") - @Inject(method = "loadContentsFromManager", at = @At("RETURN")) - private static void impl$afterLoadRegistryContents( - final ResourceManager $$0, - final RegistryOps.RegistryInfoLookup $$1, - final WritableRegistry $$2, - final Decoder $$3, - final Map, Exception> $$4, - final CallbackInfo ci) - { - if (Registries.CHAT_TYPE.equals($$2.key())) { + @Inject(method = "freezeRegistry", at = @At("HEAD")) + private void impl$beforeFreezeRegistryContents( + Map, Exception> loadingErrors, CallbackInfoReturnable cir + ) { + final RegistryLoadTask self = (RegistryLoadTask) (Object) this; + if (self instanceof NetworkRegistryLoadTask) { + // On the client side, this ought to already be registered through the network + return; + } + final var registry = ((RegistryLoadTaskAccessor) self).accessor$registry(); + if (Registries.CHAT_TYPE.equals(registry.key())) { final ChatTypeDecoration narration = ChatTypeDecoration.withSender("chat.type.text.narrate"); - $$2.register(ResourceKey.create($$2.key(), (ResourceLocation) (Object) ChatTypes.CUSTOM_CHAT.location()), (E) new ChatType(ChatTypeDecoration.withSender("%s%s"), narration), RegistrationInfo.BUILT_IN); - $$2.register(ResourceKey.create($$2.key(), (ResourceLocation) (Object) ChatTypes.CUSTOM_MESSAGE.location()), (E) new ChatType(ChatTypeDecoration.teamMessage("%s%s%s"), narration), RegistrationInfo.BUILT_IN); + registry.register(ResourceKey.create(registry.key(), (Identifier) (Object) ChatTypes.CUSTOM_CHAT.location()), (E) new ChatType(ChatTypeDecoration.withSender("%s%s"), narration), RegistrationInfo.BUILT_IN); + registry.register(ResourceKey.create(registry.key(), (Identifier) (Object) ChatTypes.CUSTOM_MESSAGE.location()), (E) new ChatType(ChatTypeDecoration.teamMessage("%s%s%s"), narration), RegistrationInfo.BUILT_IN); } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/MinecraftServerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/MinecraftServerMixin.java index 5879eff6c3e..98904805a6a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/MinecraftServerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/MinecraftServerMixin.java @@ -24,52 +24,47 @@ */ package org.spongepowered.common.mixin.core.server; -import com.google.inject.Injector; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.sugar.Local; import com.mojang.datafixers.DataFixer; import net.kyori.adventure.resource.ResourcePackRequest; -import net.minecraft.Util; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.network.chat.ChatDecorator; import net.minecraft.network.chat.Component; -import net.minecraft.obfuscate.DontObfuscate; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerFunctionManager; import net.minecraft.server.Services; import net.minecraft.server.WorldStem; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.progress.ChunkProgressListener; -import net.minecraft.server.level.progress.ChunkProgressListenerFactory; +import net.minecraft.server.level.progress.LevelLoadListener; +import net.minecraft.server.notifications.NotificationManager; import net.minecraft.server.packs.repository.PackRepository; import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.server.packs.resources.SimpleReloadInstance; -import net.minecraft.server.players.GameProfileCache; import net.minecraft.server.players.PlayerList; -import net.minecraft.util.Unit; import net.minecraft.util.thread.BlockableEventLoop; import net.minecraft.world.Difficulty; import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.storage.ServerLevelData; -import net.minecraft.world.level.storage.WorldData; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; -import org.spongepowered.api.Game; import org.spongepowered.api.Sponge; import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.EventContext; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.world.LoadWorldEvent; import org.spongepowered.api.event.world.UnloadWorldEvent; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.SubjectProxy; import org.spongepowered.api.world.DefaultWorldKeys; @@ -83,19 +78,20 @@ import org.spongepowered.asm.mixin.injection.Constant; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyConstant; -import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.SpongeServer; -import org.spongepowered.common.accessor.server.ServerFunctionManagerAccessor; +import org.spongepowered.common.accessor.server.MinecraftServerAccessor; +import org.spongepowered.common.accessor.util.BlockableEventLoopAccessor; +import org.spongepowered.common.adventure.NativeComponentRenderer; import org.spongepowered.common.bridge.commands.CommandSourceBridge; import org.spongepowered.common.bridge.commands.CommandSourceProviderBridge; import org.spongepowered.common.bridge.network.chat.SpongeChatDecorator; import org.spongepowered.common.bridge.server.MinecraftServerBridge; -import org.spongepowered.common.bridge.server.level.ServerLevelBridge; +import org.spongepowered.common.bridge.server.packs.resources.ResourceManagerBridge; import org.spongepowered.common.bridge.server.players.GameProfileCacheBridge; import org.spongepowered.common.bridge.server.players.PlayerListBridge; import org.spongepowered.common.bridge.world.level.storage.PrimaryLevelDataBridge; @@ -103,22 +99,25 @@ import org.spongepowered.common.config.SpongeGameConfigs; import org.spongepowered.common.config.inheritable.InheritableConfigHandle; import org.spongepowered.common.config.inheritable.WorldConfig; -import org.spongepowered.common.datapack.SpongeDataPackManager; +import org.spongepowered.common.event.lifecycle.FreezeRegistryEventImpl; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.event.tracking.phase.generation.GenerationPhase; +import org.spongepowered.common.launch.Launch; import org.spongepowered.common.launch.config.common.AutoSaveOptions; import org.spongepowered.common.launch.config.core.SpongeConfigs; +import org.spongepowered.common.registry.RegistryHolderLogic; +import org.spongepowered.common.registry.SpongeRegistryHolder; import org.spongepowered.common.service.server.SpongeServerScopedServiceProvider; +import org.spongepowered.common.user.SpongeUserManager; import org.spongepowered.common.util.AutoSaveMapQueue; +import org.spongepowered.common.world.server.SpongeLevelMigration; import org.spongepowered.common.world.server.SpongeWorldManager; -import java.io.IOException; import java.net.Proxy; -import java.util.Collection; -import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Set; -import java.util.concurrent.CompletableFuture; @Mixin(MinecraftServer.class) public abstract class MinecraftServerMixin implements SpongeServer, MinecraftServerBridge, CommandSourceProviderBridge, SubjectProxy, @@ -128,23 +127,19 @@ public abstract class MinecraftServerMixin implements SpongeServer, MinecraftSer @Shadow @Final private Map, ServerLevel> levels; @Shadow @Final private static Logger LOGGER; @Shadow private int tickCount; - @Shadow @Final protected LevelStorageSource.LevelStorageAccess storageSource; @Shadow @Final private Thread serverThread; @Shadow @Final private ServerFunctionManager functionManager; + @Shadow private volatile boolean isSaving; @Shadow public abstract CommandSourceStack shadow$createCommandSourceStack(); @Shadow public abstract Iterable shadow$getAllLevels(); @Shadow public abstract boolean shadow$isDedicatedServer(); @Shadow public abstract boolean shadow$isRunning(); @Shadow public abstract PlayerList shadow$getPlayerList(); - @Shadow public abstract PackRepository shadow$getPackRepository(); @Shadow public abstract RegistryAccess.Frozen shadow$registryAccess(); - @Shadow public abstract GameProfileCache shadow$getProfileCache(); - @Shadow public abstract CompletableFuture shadow$reloadResources(final Collection $$0); - @Shadow public abstract WorldData shadow$getWorldData(); - @Shadow public abstract boolean shadow$haveTime(); - @Shadow private volatile boolean isSaving; + @Shadow protected abstract boolean shadow$haveTime(); @Shadow public abstract ResourceManager shadow$getResourceManager(); + @Shadow public abstract Services shadow$services(); @Shadow @Nullable public abstract ServerLevel shadow$getLevel(ResourceKey $$0); @Shadow public abstract boolean shadow$isShutdown(); // @formatter:on @@ -152,14 +147,14 @@ public abstract class MinecraftServerMixin implements SpongeServer, MinecraftSer private final ChatDecorator impl$spongeDecorator = new SpongeChatDecorator(); private @Nullable SpongeServerScopedServiceProvider impl$serviceProvider; protected @Nullable ResourcePackRequest impl$resourcePack; - private final BlockableEventLoop impl$spongeMainThreadExecutor = new BlockableEventLoop<>("Sponge") { + private final BlockableEventLoop impl$spongeMainThreadExecutor = new BlockableEventLoop<>("Sponge", false) { //Used to schedule internal Sponge tasks to the main thread //that could be joined on the main thread. Avoiding using the //MinecraftServer Executor to prevent changes in timings. @Override - protected @NonNull Runnable wrapRunnable(@NonNull Runnable runnable) { + public Runnable wrapRunnable(Runnable runnable) { return runnable; } @@ -173,6 +168,7 @@ protected boolean shouldRun(@NonNull Runnable runnable) { return MinecraftServerMixin.this.serverThread; } }; + private RegistryHolderLogic impl$registryHolder; private final AutoSaveMapQueue impl$worldConfigSaveQueue = new AutoSaveMapQueue<>((k, log) -> { final @Nullable ServerLevel level = this.shadow$getLevel(SpongeWorldManager.createRegistryKey(k)); @@ -187,7 +183,7 @@ protected boolean shouldRun(@NonNull Runnable runnable) { final @Nullable ServerLevel level = this.shadow$getLevel(SpongeWorldManager.createRegistryKey(k)); if (level != null) { if (log) { - MinecraftServerMixin.LOGGER.info("Saving chunks for level '{}'/{}", level, level.dimension().location()); + MinecraftServerMixin.LOGGER.info("Saving chunks for level '{}'/{}", level, level.dimension().identifier()); } level.save(null, false, level.noSave); return true; @@ -202,12 +198,13 @@ public Subject subject() { @Inject(method = "", at = @At("RETURN")) private void impl$setThreadOnServerPhaseTracker( - Thread thread, LevelStorageSource.LevelStorageAccess storageAccess, PackRepository packRepo, - WorldStem stem, Proxy proxy, DataFixer fixer, - Services services, ChunkProgressListenerFactory progress, CallbackInfo ci + Thread serverThread, LevelStorageSource.LevelStorageAccess storageSource, PackRepository packRepository, + WorldStem worldStem, Optional gameRules, Proxy proxy, DataFixer fixerUpper, Services services, + LevelLoadListener levelLoadListener, boolean propagatesCrashes, + NotificationManager notificationManager, CallbackInfo ci ) { try { - PhaseTracker.SERVER.setThread(thread); + PhaseTracker.getServerInstanceExplicitly().setThread(serverThread); } catch (final IllegalAccessException e) { throw new RuntimeException("Could not initialize the server PhaseTracker!"); } @@ -237,8 +234,27 @@ public Subject subject() { frame.pushCause(Sponge.systemSubject()); } - @Inject(method = "createLevels", at = @At(value = "INVOKE", ordinal = 0, - target = "Lnet/minecraft/server/level/ServerLevel;(Lnet/minecraft/server/MinecraftServer;Ljava/util/concurrent/Executor;Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;Lnet/minecraft/world/level/storage/ServerLevelData;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/level/dimension/LevelStem;Lnet/minecraft/server/level/progress/ChunkProgressListener;ZJLjava/util/List;ZLnet/minecraft/world/RandomSequences;)V")) + /** + * Writes migrated SpongeRegistryData files AFTER FileFixerUpper.fix() has completed + * (the COW swap is done, directory structure is final) but BEFORE any ServerLevel is created. + * This ensures SavedDataStorage.computeIfAbsent() finds the files on disk. + */ + @Inject(method = "loadLevel", at = @At("HEAD")) + private void impl$writeMigratedSpongeRegistryData(final CallbackInfo ci) { + SpongeLevelMigration.writeCachedRegistryData( + ((MinecraftServerAccessor) this).accessor$storageSource().getLevelDirectory().path() + ); + } + + @Inject(method = "createLevels", at = @At(value = "NEW", + target = "(Lnet/minecraft/server/MinecraftServer;Ljava/util/concurrent/Executor;Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;Lnet/minecraft/world/level/storage/ServerLevelData;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/level/dimension/LevelStem;ZJLjava/util/List;Z)Lnet/minecraft/server/level/ServerLevel;" + ), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/core/Registry;getValue(Lnet/minecraft/resources/ResourceKey;)Ljava/lang/Object;"), + to = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/storage/SavedDataStorage;computeIfAbsent(Lnet/minecraft/world/level/saveddata/SavedDataType;)Lnet/minecraft/world/level/saveddata/SavedData;", + ordinal = 0 + ) + )) private void impl$onCreateDefaultLevel(final CallbackInfo ci, @Local final ServerLevelData levelData, @Local final LevelStem levelStem) { ((PrimaryLevelDataBridge) levelData).bridge$populateFromLevelStem(levelStem); ((ServerLevelDataBridge) levelData).bridge$spongeData().setKey(DefaultWorldKeys.DEFAULT); @@ -252,33 +268,31 @@ public Subject subject() { } @Redirect(method = "createLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/Registry;entrySet()Ljava/util/Set;")) - private Set impl$onCreateOtherLevels(final Registry stemRegistry) { + private Set, LevelStem>> impl$onCreateOtherLevels(final Registry stemRegistry) { this.worldManager().createNonDefaultLevels(); - return Set.of(); // prevent vanilla code + // prevent vanilla code, except for the overworld as its special cased + return Set.of(Map.entry(LevelStem.OVERWORLD, stemRegistry.getValueOrThrow(LevelStem.OVERWORLD))); } @WrapMethod(method = "setInitialSpawn") - private static void impl$wrapSetInitialSpawn(final ServerLevel level, final ServerLevelData levelData, final boolean generateBonusChest, final boolean debugWorld, final Operation original) { + private static void impl$wrapSetInitialSpawn( + final ServerLevel level, final ServerLevelData levelData, final boolean generateBonusChest, + final boolean debugWorld, final LevelLoadListener listener, Operation original) { try (final var state = GenerationPhase.State.TERRAIN_GENERATION.createPhaseContext(PhaseTracker.getInstance())) { state.buildAndSwitch(); - original.call(level, levelData, generateBonusChest, debugWorld); + original.call(level, levelData, generateBonusChest, debugWorld, listener); } } - /** - * @author Yeregorix - * @reason Multi world. - */ - @Overwrite - private void prepareLevels(final ChunkProgressListener progressListener) { - this.worldManager().prepareLevels(); + @Inject(method = "prepareLevels", at = @At("TAIL")) + private void impl$onPrepareLevels(final CallbackInfo ci) { + ((SpongeUserManager) Sponge.server().userManager()).init(); } /** * @author Zidane * @reason Apply our branding */ - @DontObfuscate @Overwrite public String getServerModName() { return "sponge"; @@ -286,27 +300,15 @@ public String getServerModName() { @Inject(method = "stopServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;saveAllChunks(ZZZ)Z")) private void impl$callUnloadWorldEvents(final CallbackInfo ci) { - for(final ServerLevel level : this.shadow$getAllLevels()) { + for (final ServerLevel level : this.shadow$getAllLevels()) { final UnloadWorldEvent unloadWorldEvent = SpongeEventFactory.createUnloadWorldEvent(PhaseTracker.getInstance().currentCause(), (ServerWorld) level); SpongeCommon.post(unloadWorldEvent); } } - @Inject(method = "stopServer", at = @At(value = "TAIL")) - private void impl$closeLevelSaveForOtherWorlds(final CallbackInfo ci) { - for (final Map.Entry, ServerLevel> entry : this.levels.entrySet()) { - if (entry.getKey() == Level.OVERWORLD) { - continue; - } - - final LevelStorageSource.LevelStorageAccess levelSave = ((ServerLevelBridge) entry.getValue()).bridge$getLevelSave(); - try { - levelSave.close(); - } catch (final IOException e) { - MinecraftServerMixin.LOGGER.error("Failed to unlock level {}", levelSave.getLevelId(), e); - } - } - } + // Phase 1+2: All worlds now share the server's single LevelStorageAccess. + // The server's storageSource is closed by vanilla's stopServer() itself, + // so we no longer need to close per-world storage access instances. /** * Render localized/formatted chat components @@ -315,15 +317,17 @@ public String getServerModName() { */ @Inject(method = "sendSystemMessage", at = @At("HEAD"), cancellable = true) private void impl$useTranslatingLogger(final Component input, final CallbackInfo ci) { - MinecraftServerMixin.LOGGER.info(input.getString()); + MinecraftServerMixin.LOGGER.info(NativeComponentRenderer.apply(input, Locale.getDefault(), Sponge.game().systemSubject()).getString()); ci.cancel(); } - @ModifyConstant(method = "tickServer", constant = @Constant(intValue = 0, ordinal = 0, expandZeroConditions = Constant.Condition.LESS_THAN_OR_EQUAL_TO_ZERO), + @ModifyConstant(method = "tickServer", slice = @Slice( - from = @At(value = "FIELD", target = "Lnet/minecraft/server/MinecraftServer;ticksUntilAutosave:I"), - to = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;computeNextAutosaveInterval()I"))) - private int getSaveTickInterval(final int zero) { + to = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;autoSave()V", ordinal = 1), + from = @At(value = "FIELD", target = "Lnet/minecraft/server/MinecraftServer;ticksUntilAutosave:I", ordinal = 0) + ), + constant = @Constant(intValue = 0, ordinal = 0, expandZeroConditions = Constant.Condition.LESS_THAN_OR_EQUAL_TO_ZERO)) + private int impl$getSaveTickInterval(final int zero) { if (!this.shadow$isDedicatedServer()) { return zero; } else if (!this.shadow$isRunning()) { @@ -414,7 +418,7 @@ public boolean saveAllChunks(final boolean suppressLog, final boolean flush, fin // Sponge end if (autoSave.log) { - LOGGER.info("Saving chunks for level '{}'/{}", level, level.dimension().location()); + LOGGER.info("Saving chunks for level '{}'/{}", level, level.dimension().identifier()); } level.save(null, flush, level.noSave && !isForced); @@ -425,7 +429,7 @@ public boolean saveAllChunks(final boolean suppressLog, final boolean flush, fin // ServerLevel var2 = this.overworld(); // ServerLevelData var3 = this.worldData.overworldData(); // var3.setWorldBorder(var2.getWorldBorder().createSettings()); -// this.worldData.setCustomBossEvents(this.getCustomBossEvents().save(this.registryAccess())); +// this.worldData.setCustomBossEvents(this.customBossEvents().save(this.registryAccess())); // this.storageSource.saveDataTag(this.registryHolder, this.worldData, this.shadow$getPlayerList().getSingleplayerData()); // Sponge end @@ -433,7 +437,7 @@ public boolean saveAllChunks(final boolean suppressLog, final boolean flush, fin // Save the usercache.json file every 10 minutes or if forced to if (isForced || this.tickCount % 6000 == 0) { // We want to save the username cache json, as we normally bypass it. - final GameProfileCache profileCache = this.shadow$getProfileCache(); + final var profileCache = this.shadow$services().nameToIdCache(); ((GameProfileCacheBridge) profileCache).bridge$setCanSave(true); profileCache.save(); ((GameProfileCacheBridge) profileCache).bridge$setCanSave(false); @@ -487,31 +491,11 @@ public void setDifficulty(final Difficulty difficulty, final boolean force) { } } - @Override - public void bridge$initServices(final Game game, final Injector injector) { - if (this.impl$serviceProvider == null) { - this.impl$serviceProvider = new SpongeServerScopedServiceProvider(this, game, injector); - this.impl$serviceProvider.init(); - - Util.blockUntilDone(e -> - SimpleReloadInstance.of(this.shadow$getResourceManager(), List.of(((ServerFunctionManagerAccessor) this.functionManager).accessor$library()), - Util.backgroundExecutor(), e, CompletableFuture.completedFuture(Unit.INSTANCE)).done()); - } - } - @Override public SpongeServerScopedServiceProvider bridge$getServiceProvider() { return this.impl$serviceProvider; } - @ModifyVariable(method = "reloadResources", at = @At(value = "HEAD"), argsOnly = true) - public Collection impl$reloadResources(final Collection datapacksToLoad) { - final List reloadablePacks = ((SpongeDataPackManager) this.dataPackManager()).registerPacks(); - reloadablePacks.addAll(datapacksToLoad); - this.shadow$getPackRepository().reload(); - return reloadablePacks; - } - @Override public String toString() { return this.getClass().getSimpleName(); @@ -532,11 +516,41 @@ public String toString() { @Inject(method = "pollTaskInternal", at = @At("HEAD"), cancellable = true) private void impl$pollSpongeTasks(final CallbackInfoReturnable cir) { //Pool our tasks first to try to have small impact on timings - if (this.impl$spongeMainThreadExecutor.pollTask()) { + if (((BlockableEventLoopAccessor) this.impl$spongeMainThreadExecutor).invoker$pollTask()) { cir.setReturnValue(true); } } + @Override + public void bridge$reloadServerRegistries(final RegistryHolder holder) { + ((SpongeRegistryHolder) holder).setRootMinecraftRegistry(this.shadow$registryAccess()); + Launch.instance().lifecycle().beginEstablishServerRegistries(holder); + } + + @Inject(method = "", at = @At("TAIL")) + public void impl$onInit(final CallbackInfo ci, final @Local(argsOnly = true) WorldStem levelStem) { + final SpongeRegistryHolder rmHolder = (SpongeRegistryHolder) levelStem.resourceManager(); + // The integrated server's "create new world" path constructs a fresh resource manager + // in WorldOpenFlows.createLevelFromExistingSettings that bypasses + // ReloadableServerResourcesMixin#impl$onLoadResources, so its RegistryHolderLogic has + // no Minecraft root populated. Seed it from the server's frozen registry access — for + // the dedicated path this is a no-op since the same data was already wired. + rmHolder.setRootMinecraftRegistry(this.shadow$registryAccess()); + this.bridge$reloadedServerRegistries(rmHolder.registryHolder()); + this.impl$serviceProvider = ((ResourceManagerBridge) levelStem.resourceManager()).bridge$services(); + } + + @Override + public void bridge$reloadedServerRegistries(final RegistryHolderLogic holder) { + this.impl$registryHolder = holder; + Sponge.game().eventManager().post(FreezeRegistryEventImpl.PostImpl.EngineImpl.server(Cause.of(EventContext.empty(), Sponge.game()), Sponge.game(), holder)); + } + + @Override + public RegistryHolderLogic bridge$registryHolder() { + return this.impl$registryHolder; + } + @ModifyExpressionValue(method = "scheduleExecutables", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;isStopped()Z")) private boolean impl$scheduleExecutablesIsShutdown(final boolean original) { // Vanilla immediately flips this bit when the shutdown starts diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/PlayerAdvancementsMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/PlayerAdvancementsMixin.java index a7e2dc9b4ce..dbaa32942f7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/PlayerAdvancementsMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/PlayerAdvancementsMixin.java @@ -159,7 +159,7 @@ public abstract class PlayerAdvancementsMixin implements PlayerAdvancementsBridg } } - @Redirect(method = "lambda$award$2", + @Redirect(method = "lambda$award$0", at = @At( value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Z)V")) @@ -180,6 +180,7 @@ public abstract class PlayerAdvancementsMixin implements PlayerAdvancementsBridg at = @At( value = "INVOKE", target = "Lnet/minecraft/server/PlayerAdvancements;markForVisibilityUpdate(Lnet/minecraft/advancements/AdvancementHolder;)V")) + @SuppressWarnings("deprecation") private void impl$callGrantEventIfSuccessful(final AdvancementHolder advancement, final String string, final CallbackInfoReturnable ci) { if (!this.impl$wasSuccess) { return; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/ReloadableServerRegistriesMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/ReloadableServerRegistriesMixin.java deleted file mode 100644 index 4671fc49048..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/ReloadableServerRegistriesMixin.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.server; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.core.LayeredRegistryAccess; -import net.minecraft.core.RegistryAccess; -import net.minecraft.server.RegistryLayer; -import net.minecraft.server.ReloadableServerRegistries; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.common.launch.Launch; - -@Mixin(ReloadableServerRegistries.class) -public abstract class ReloadableServerRegistriesMixin { - - @WrapOperation(method = "reload", at = @At(value = "INVOKE", - target = "Lnet/minecraft/core/LayeredRegistryAccess;getAccessForLoading(Ljava/lang/Object;)Lnet/minecraft/core/RegistryAccess$Frozen;")) - private static RegistryAccess.Frozen impl$onGetRegistryAccess(final LayeredRegistryAccess instance, final Object layer, final Operation original) { - final RegistryAccess.Frozen registryAccess = original.call(instance, layer); - final var lifecycle = Launch.instance().lifecycle(); - lifecycle.establishGlobalRegistries(registryAccess, (RegistryLayer) layer); - return registryAccess; - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/ReloadableServerResourcesMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/ReloadableServerResourcesMixin.java new file mode 100644 index 00000000000..4d95e242a0b --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/ReloadableServerResourcesMixin.java @@ -0,0 +1,114 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.server; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import net.minecraft.commands.Commands; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.LayeredRegistryAccess; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.server.RegistryLayer; +import net.minecraft.server.ReloadableServerRegistries; +import net.minecraft.server.ReloadableServerResources; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.permissions.PermissionSet; +import net.minecraft.world.flag.FeatureFlagSet; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryRoots; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.accessor.core.MappedRegistryAccessor; +import org.spongepowered.common.bridge.server.ReloadableServerResourcesBridge; +import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.registry.SpongeRegistryHolder; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.stream.Stream; + +@Mixin(ReloadableServerResources.class) +public abstract class ReloadableServerResourcesMixin implements ReloadableServerResourcesBridge { + + private @Nullable ResourceManager impl$resourceManager; + + @Override + public void bridge$resourceManager(final ResourceManager resourceManager) { + this.impl$resourceManager = resourceManager; + } + + @Override + public @Nullable ResourceManager bridge$resourceManager() { + return this.impl$resourceManager; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @ModifyExpressionValue(method = "loadResources", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ReloadableServerRegistries;reload(Lnet/minecraft/core/LayeredRegistryAccess;Ljava/util/List;Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;")) + private static CompletableFuture impl$onLoadResources( + final CompletableFuture original, final ResourceManager $$0, + final LayeredRegistryAccess $$1, final List> $$2, + final FeatureFlagSet $$3, final Commands.CommandSelection $$4, final PermissionSet $$5, + final Executor $$6, final Executor $$7 + ) { + return original.thenApply(r -> { + final SpongeRegistryHolder spongeRegistryHolder = (SpongeRegistryHolder) $$0; + spongeRegistryHolder.setRootMinecraftRegistry(r.layers().compositeAccess()); + spongeRegistryHolder.registryHolder().freezeSpongeDynamicRegistries(false); + + final List spongeRegistries = (List) Stream.concat( + spongeRegistryHolder.streamRegistries(RegistryRoots.SPONGE), + Sponge.game().streamRegistries(RegistryRoots.SPONGE)).toList(); + + final RegistryAccess.Frozen reloadable = r.layers().getLayer(RegistryLayer.RELOADABLE); + final RegistryAccess.Frozen combined = new RegistryAccess.ImmutableRegistryAccess((Stream) Stream.concat( + reloadable.registries(), + spongeRegistries.stream().filter(v -> ((MappedRegistryAccessor) v).accessor$frozen()).map(v -> new RegistryAccess.RegistryEntry<>(v.key(), v)) + )).freeze(); + + return new ReloadableServerRegistries.LoadResult( + r.layers().replaceFrom(RegistryLayer.RELOADABLE, combined), + HolderLookup.Provider.create((Stream) Stream.concat(r.lookupWithUpdatedTags().listRegistries(), spongeRegistries.stream())) + ); + }); + } + + @ModifyReturnValue(method = "loadResources", at = @At(value = "RETURN")) + private static CompletableFuture impl$onLoaded( + final CompletableFuture original, final ResourceManager $$0, + final LayeredRegistryAccess $$1, final List> $$2, + final FeatureFlagSet $$3, final Commands.CommandSelection $$4, final PermissionSet $$5, + final Executor $$6, final Executor $$7 + ) { + return original.thenApply(r -> { + ((ReloadableServerResourcesBridge) r).bridge$resourceManager($$0); + Launch.instance().lifecycle().endEstablishServerRegistries((RegistryHolder) $$0); + return r; + }); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/ServerAdvancementManagerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/ServerAdvancementManagerMixin.java new file mode 100644 index 00000000000..9b835c58b54 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/ServerAdvancementManagerMixin.java @@ -0,0 +1,81 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.server; + +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementNode; +import net.minecraft.resources.Identifier; +import net.minecraft.server.ServerAdvancementManager; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.profiling.ProfilerFiller; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.advancement.AdvancementTree; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.advancement.AdvancementTreeEvent; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryEntry; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.registry.RegistryHolderLogic; +import org.spongepowered.common.registry.SpongeRegistryHolder; + +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Mixin(ServerAdvancementManager.class) +public abstract class ServerAdvancementManagerMixin { + + @SuppressWarnings({"unchecked"}) + @WrapMethod(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V") + private void impl$onApply(final Map $$0, final ResourceManager $$1, final ProfilerFiller $$2, final Operation original) { + final RegistryHolderLogic registryHolder = ((SpongeRegistryHolder) $$1).registryHolder(); + final Registry registry = (Registry) (Object) registryHolder.registry(RegistryTypes.ADVANCEMENT); + $$0.forEach((k, v) -> registry.register((ResourceKey) (Object) k, v)); + Launch.instance().lifecycle().processServerRegistries((RegistryHolder) $$1, Stream.of(registry)); + original.call(registry.streamEntries().collect(Collectors.toMap(RegistryEntry::key, RegistryEntry::value)), $$1, $$2); + } + + @WrapOperation(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/advancements/TreeNodePosition;run(Lnet/minecraft/advancements/AdvancementNode;)V")) + private void impl$onLayout(final AdvancementNode instance, final Operation original, + final Map $$0, final ResourceManager $$1, final ProfilerFiller $$2) { + original.call(instance); + + final Cause cause = PhaseTracker.getInstance().currentCause(); + final AdvancementTreeEvent.GenerateLayout event = SpongeEventFactory.createAdvancementTreeEventGenerateLayout( + cause, ((SpongeRegistryHolder) $$1).registryHolder(), (AdvancementTree) instance); + SpongeCommon.post(event); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/ServerFunctionLibraryMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/ServerFunctionLibraryMixin.java deleted file mode 100644 index 32256d2dccd..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/ServerFunctionLibraryMixin.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.server; - -import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import net.minecraft.server.ServerFunctionLibrary; -import net.minecraft.server.packs.resources.PreparableReloadListener; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.util.profiling.ProfilerFiller; -import org.spongepowered.api.Sponge; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; - -@Mixin(ServerFunctionLibrary.class) -public abstract class ServerFunctionLibraryMixin { - - @WrapMethod(method = "reload") - private CompletableFuture impl$onReload(final PreparableReloadListener.PreparationBarrier $$0, final ResourceManager $$1, final ProfilerFiller $$2, - final ProfilerFiller $$3, final Executor $$4, final Executor $$5, final Operation> original) { - if (Sponge.isServerAvailable()) { - return original.call($$0, $$1, $$2, $$3, $$4, $$5); - } - return $$0.wait(null); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/WorldLoaderMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/WorldLoaderMixin.java index be1b495c641..9433192c67d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/WorldLoaderMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/WorldLoaderMixin.java @@ -27,33 +27,55 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import com.mojang.datafixers.util.Pair; -import net.minecraft.core.LayeredRegistryAccess; -import net.minecraft.core.RegistryAccess; -import net.minecraft.server.RegistryLayer; import net.minecraft.server.WorldLoader; import net.minecraft.server.packs.resources.CloseableResourceManager; +import net.minecraft.server.permissions.LevelBasedPermissionSet; +import net.minecraft.server.permissions.PermissionLevel; +import net.minecraft.server.permissions.PermissionSet; import net.minecraft.world.level.WorldDataConfiguration; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.bridge.core.WritableRegistryBridge; import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.launch.Lifecycle; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Supplier; + @Mixin(WorldLoader.class) public abstract class WorldLoaderMixin { - @ModifyExpressionValue(method = "load", at = @At(value = "INVOKE", - target = "Lnet/minecraft/server/WorldLoader$PackConfig;createResourceManager()Lcom/mojang/datafixers/util/Pair;")) - private static Pair impl$onCreateResourceManager(final Pair pair) { - Launch.instance().lifecycle().setWorldDataConfiguration(pair.getFirst()); - return pair; + @WrapOperation(method = "load(Lnet/minecraft/server/WorldLoader$InitConfig;Lnet/minecraft/server/WorldLoader$WorldDataSupplier;Lnet/minecraft/server/WorldLoader$ResultFactory;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;", at = @At(value = "INVOKE", + target = "Ljava/util/concurrent/CompletableFuture;supplyAsync(Ljava/util/function/Supplier;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;", remap = false)) + private static CompletableFuture> impl$onCreateResourceManager( + final Supplier> supplier, final Executor executor, final Operation>> original, + final @Local(argsOnly = true) WorldLoader.InitConfig $$0) { + return original.call(supplier, executor).thenApply(pair -> { + final CloseableResourceManager resourceManager = pair.getSecond(); + final Lifecycle lifecycle = Launch.instance().lifecycle(); + var permLevel = $$0.functionCompilationPermissions() == PermissionSet.ALL_PERMISSIONS ? PermissionLevel.OWNERS : PermissionLevel.ALL; + if ($$0.functionCompilationPermissions() instanceof LevelBasedPermissionSet lbps) { + permLevel = lbps.level(); + } + lifecycle.establishServerServices(resourceManager, permLevel); + lifecycle.setWorldDataConfiguration(pair.getFirst()); + lifecycle.beginEstablishServerRegistries((RegistryHolder) resourceManager); + return pair; + }); } - @WrapOperation(method = "load", at = @At(value = "INVOKE", - target = "Lnet/minecraft/core/LayeredRegistryAccess;getAccessForLoading(Ljava/lang/Object;)Lnet/minecraft/core/RegistryAccess$Frozen;")) - private static RegistryAccess.Frozen impl$onGetRegistryAccess(final LayeredRegistryAccess instance, final Object layer, final Operation original) { - final RegistryAccess.Frozen registryAccess = original.call(instance, layer); - final var lifecycle = Launch.instance().lifecycle(); - lifecycle.establishGlobalRegistries(registryAccess, (RegistryLayer) layer); - return registryAccess; + @ModifyExpressionValue(method = "lambda$load$2", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/WorldLoader$WorldDataSupplier;get(Lnet/minecraft/server/WorldLoader$DataLoadContext;)Lnet/minecraft/server/WorldLoader$DataLoadOutput;")) + private static WorldLoader.DataLoadOutput impl$onBakedDimensionRegistries(final WorldLoader.DataLoadOutput original, + final @Local CloseableResourceManager resourceManager) { + original.finalDimensions().registries().forEach(r -> ((WritableRegistryBridge) r.value()).bridge$unfreeze()); + Launch.instance().lifecycle().processServerRegistries((RegistryHolder) resourceManager, original.finalDimensions().registries().map(e -> (Registry) e.value())); + original.finalDimensions().registries().forEach(r -> r.value().freeze()); + return original; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/bossevents/CustomBossEventMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/bossevents/CustomBossEventMixin.java index 2ab91a5237f..1ca1d10d932 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/bossevents/CustomBossEventMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/bossevents/CustomBossEventMixin.java @@ -24,35 +24,21 @@ */ package org.spongepowered.common.mixin.core.server.bossevents; -import net.minecraft.network.chat.Component; import net.minecraft.server.bossevents.CustomBossEvent; +import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.mixin.core.server.level.ServerBossEventMixin; @Mixin(CustomBossEvent.class) public abstract class CustomBossEventMixin extends ServerBossEventMixin { @Shadow private int max; - @Redirect(method = {"getValue", "save"}, - at = @At(value = "FIELD", target = "Lnet/minecraft/server/bossevents/CustomBossEvent;value:I")) + @Redirect(method = {"value"}, + at = @At(value = "FIELD", target = "Lnet/minecraft/server/bossevents/CustomBossEvent;value:I", opcode = Opcodes.GETFIELD)) private int impl$valueRead(final CustomBossEvent $this) { return (int) (this.bridge$asAdventure().progress() * this.max); } - - @Redirect( - method = {"save"}, - at = @At( - value = "FIELD", - target = "Lnet/minecraft/server/bossevents/CustomBossEvent;name:Lnet/minecraft/network/chat/Component;" - ) - ) - private Component impl$nameRead(final CustomBossEvent $this) { - return SpongeAdventure.asVanilla(this.bridge$asAdventure().name()); - } - - // Value writes already update the percent field of superclasses, so we don't need to redirect } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/DifficultyCommandMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/DifficultyCommandMixin.java index d078c048e3a..50c8cf45de0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/DifficultyCommandMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/DifficultyCommandMixin.java @@ -31,6 +31,7 @@ import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket; import net.minecraft.server.commands.DifficultyCommand; import net.minecraft.world.Difficulty; +import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.storage.LevelData; import org.spongepowered.api.world.server.storage.ServerWorldProperties; import org.spongepowered.asm.mixin.Final; @@ -38,13 +39,12 @@ import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.accessor.server.MinecraftServerAccessor; @Mixin(DifficultyCommand.class) public abstract class DifficultyCommandMixin { // @formatter:off - @Shadow @Final private static DynamicCommandExceptionType ERROR_ALREADY_DIFFICULT; + @Shadow @Final private static DynamicCommandExceptionType ERROR_ALREADY_SAME_DIFFICULTY; // @formatter:on /** @@ -54,11 +54,11 @@ public abstract class DifficultyCommandMixin { @Overwrite public static int setDifficulty(CommandSourceStack source, Difficulty difficulty) throws CommandSyntaxException { if (source.getLevel().getDifficulty() == difficulty) { - throw DifficultyCommandMixin.ERROR_ALREADY_DIFFICULT.create(difficulty.getKey()); + throw DifficultyCommandMixin.ERROR_ALREADY_SAME_DIFFICULTY.create(difficulty.getSerializedName()); } else { final LevelData levelData = source.getLevel().getLevelData(); ((ServerWorldProperties) levelData).setDifficulty((org.spongepowered.api.world.difficulty.Difficulty) (Object) difficulty); - source.getLevel().setSpawnSettings(((MinecraftServerAccessor) SpongeCommon.server()).invoker$isSpawningMonsters(), SpongeCommon.server().isSpawningAnimals()); + source.getLevel().setSpawnSettings(SpongeCommon.server().getGameRules().get(GameRules.SPAWN_MONSTERS)); source.getLevel().getPlayers(p -> true).forEach(p -> p.connection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked()))); source.sendSuccess(() -> Component.translatable("commands.difficulty.success", difficulty.getDisplayName()), true); diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/commands/SpreadPlayersCommandMixin_Vanilla.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/SpreadPlayersCommandMixin.java similarity index 71% rename from vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/commands/SpreadPlayersCommandMixin_Vanilla.java rename to src/mixins/java/org/spongepowered/common/mixin/core/server/commands/SpreadPlayersCommandMixin.java index e7199d7e2b2..3c0183b28e8 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/commands/SpreadPlayersCommandMixin_Vanilla.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/SpreadPlayersCommandMixin.java @@ -22,33 +22,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.mixin.core.server.commands; +package org.spongepowered.common.mixin.core.server.commands; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.server.commands.SpreadPlayersCommand; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.RelativeMovement; +import net.minecraft.world.entity.Relative; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.cause.entity.MovementTypes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.event.tracking.PhaseTracker; import java.util.Set; @Mixin(SpreadPlayersCommand.class) -public abstract class SpreadPlayersCommandMixin_Vanilla { +public abstract class SpreadPlayersCommandMixin { - @Redirect(method = "setPlayerPositions", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;teleportTo(Lnet/minecraft/server/level/ServerLevel;DDDLjava/util/Set;FF)Z")) + @WrapOperation(method = "setPlayerPositions", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;teleportTo(Lnet/minecraft/server/level/ServerLevel;DDDLjava/util/Set;FFZ)Z")) private static boolean vanilla$createCauseFrameForTeleport( - final Entity instance, final ServerLevel level, final double x, final double y, - final double z, final Set relativeMovements, final float yRot, final float xRot) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + final Entity instance, final ServerLevel level, final double x, final double y, final double z, + final Set relativeMovements, final float yRot, final float xRot, final boolean setCamera, final Operation original) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getWorldInstance(instance.level()).pushCauseFrame()) { frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.COMMAND); - - return instance.teleportTo(level, x, y, z, relativeMovements, yRot, xRot); + return original.call(instance, level, x, y, z, relativeMovements, yRot, xRot, setCamera); } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/TeleportCommandMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/TeleportCommandMixin.java index 0513e4dc3ee..e2a9ffa9a4f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/TeleportCommandMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/TeleportCommandMixin.java @@ -24,168 +24,31 @@ */ package org.spongepowered.common.mixin.core.server.commands; -import net.minecraft.core.BlockPos; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.server.commands.TeleportCommand; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.level.TicketType; -import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.RelativeMovement; -import net.minecraft.world.level.ChunkPos; -import org.spongepowered.api.Sponge; +import net.minecraft.world.entity.Relative; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; -import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.cause.entity.MovementTypes; -import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; -import org.spongepowered.api.event.entity.MoveEntityEvent; -import org.spongepowered.api.event.entity.RotateEntityEvent; -import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.event.ShouldFire; import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.hooks.PlatformHooks; -import org.spongepowered.common.util.VecHelper; -import org.spongepowered.math.vector.Vector3d; import java.util.Set; @Mixin(TeleportCommand.class) public abstract class TeleportCommandMixin { - @Redirect(method = "performTeleport", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;teleportTo(Lnet/minecraft/server/level/ServerLevel;DDDLjava/util/Set;FF)Z")) + @WrapOperation(method = "performTeleport", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;teleportTo(Lnet/minecraft/server/level/ServerLevel;DDDLjava/util/Set;FFZ)Z")) private static boolean impl$createCauseFrameForPerformTeleport( - final Entity entityIn, final ServerLevel worldIn, final double x, final double y, - final double z, final Set relativeList, final float yaw, final float pitch - ) { - double actualX = x; - double actualY = y; - double actualZ = z; - double actualYaw = yaw; - double actualPitch = pitch; - - if (worldIn == entityIn.level()) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.COMMAND); - - // TODO Should honor the relative list before the event.. - - if (ShouldFire.MOVE_ENTITY_EVENT) { - final MoveEntityEvent posEvent = SpongeEventFactory.createMoveEntityEvent(frame.currentCause(), - (org.spongepowered.api.entity.Entity) entityIn, VecHelper.toVector3d(entityIn.position()), - new Vector3d(x, y, z), new Vector3d(x, y, z)); - - if (SpongeCommon.post(posEvent)) { - return false; - } - - actualX = posEvent.destinationPosition().x(); - actualY = posEvent.destinationPosition().y(); - actualZ = posEvent.destinationPosition().z(); - } - - if (ShouldFire.ROTATE_ENTITY_EVENT) { - final RotateEntityEvent rotateEvent = SpongeEventFactory.createRotateEntityEvent(frame.currentCause(), - (org.spongepowered.api.entity.Entity) entityIn, new Vector3d(actualPitch, actualYaw, 0), - new Vector3d(pitch, yaw, 0)); - - SpongeCommon.post(rotateEvent); - actualYaw = rotateEvent.isCancelled() ? entityIn.getYRot() : rotateEvent.toRotation().y(); - actualPitch = rotateEvent.isCancelled() ? entityIn.getXRot() : rotateEvent.toRotation().x(); - } - - if (entityIn instanceof ServerPlayer) { - - ChunkPos chunkpos = new ChunkPos(BlockPos.containing(actualX, actualY, actualZ)); - worldIn.getChunkSource().addRegionTicket(TicketType.POST_TELEPORT, chunkpos, 1, entityIn.getId()); - - entityIn.stopRiding(); - - if (((ServerPlayer)entityIn).isSleeping()) { - ((ServerPlayer)entityIn).stopSleepInBed(true, true); - } - - ((ServerPlayer)entityIn).connection.teleport(actualX, actualY, actualZ, (float) actualYaw, (float) actualPitch, relativeList); - } else { - entityIn.moveTo(actualX, actualY, actualZ, (float) actualYaw, (float) actualPitch); - } - - entityIn.setYHeadRot((float) actualYaw); - } - } else { - if (entityIn instanceof ServerPlayer) { - // To ensure mod code is caught, handling the world change for players happens in teleport - // Teleport will create a frame but we want to ensure it'll be the command movement type - // TODO check if this is still correct - PhaseTracker.getInstance().addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.COMMAND); - ((ServerPlayer) entityIn).teleportTo(worldIn, x, y, z, yaw, pitch); - PhaseTracker.getInstance().removeContext(EventContextKeys.MOVEMENT_TYPE); - } else { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.COMMAND); - - final ServerLevel fromWorld = (ServerLevel) entityIn.getCommandSenderWorld(); - - final ChangeEntityWorldEvent.Pre preEvent = PlatformHooks.INSTANCE.getEventHooks().callChangeEntityWorldEventPre(entityIn, - worldIn); - if (SpongeCommon.post(preEvent)) { - return false; - } - - final ChangeEntityWorldEvent.Reposition posEvent = - SpongeEventFactory.createChangeEntityWorldEventReposition(frame.currentCause(), - (org.spongepowered.api.entity.Entity) entityIn, - (org.spongepowered.api.world.server.ServerWorld) entityIn.getCommandSenderWorld(), - VecHelper.toVector3d(entityIn.position()), new Vector3d(x, y, z), preEvent.originalDestinationWorld(), - new Vector3d(x, y, z), preEvent.destinationWorld()); - - if (SpongeCommon.post(posEvent)) { - return false; - } - - entityIn.unRide(); - final Entity result = entityIn.getType().create(worldIn); - if (result == null) { - return false; - } - - if (ShouldFire.ROTATE_ENTITY_EVENT) { - final RotateEntityEvent rotateEvent = SpongeEventFactory.createRotateEntityEvent(frame.currentCause(), - (org.spongepowered.api.entity.Entity) entityIn, new Vector3d(entityIn.getXRot(), entityIn.getYRot(), 0), - new Vector3d(actualPitch, actualYaw, 0)); - - if (!SpongeCommon.post(rotateEvent)) { - actualYaw = Mth.wrapDegrees(rotateEvent.toRotation().y()); - actualPitch = Mth.wrapDegrees(rotateEvent.toRotation().x()); - actualPitch = Mth.clamp(actualPitch, -90.0F, 90.0F); - } else { - actualYaw = entityIn.getYRot(); - actualPitch = entityIn.getXRot(); - } - } - - result.restoreFrom(entityIn); - result.moveTo(posEvent.destinationPosition().x(), posEvent.destinationPosition().y(), - posEvent.destinationPosition().z(), (float) actualYaw, (float) actualPitch); - result.setYHeadRot((float) actualYaw); - worldIn.addDuringTeleport(result); - entityIn.setRemoved(Entity.RemovalReason.CHANGED_DIMENSION); - - Sponge.eventManager().post(SpongeEventFactory.createChangeEntityWorldEventPost( - PhaseTracker.getInstance().currentCause(), - (org.spongepowered.api.entity.Entity) result, - (ServerWorld) fromWorld, - preEvent.originalDestinationWorld(), - preEvent.destinationWorld() - )); - } - } + final Entity instance, final ServerLevel level, final double x, final double y, final double z, + final Set relativeMovements, final float yRot, final float xRot, final boolean setCamera, final Operation original) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getWorldInstance(instance.level()).pushCauseFrame()) { + frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.COMMAND); + return original.call(instance, level, x, y, z, relativeMovements, yRot, xRot, setCamera); } - - return true; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/WeatherCommandMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/WeatherCommandMixin.java index 834ff15da6e..12f99720095 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/WeatherCommandMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/WeatherCommandMixin.java @@ -24,24 +24,53 @@ */ package org.spongepowered.common.mixin.core.server.commands; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.commands.CommandSourceStack; import net.minecraft.server.MinecraftServer; import net.minecraft.server.commands.WeatherCommand; import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.saveddata.WeatherData; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(WeatherCommand.class) public abstract class WeatherCommandMixin { - @Redirect(method = { - "getDuration", + @WrapOperation( + method = "getDuration", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;getRandom()Lnet/minecraft/util/RandomSource;") + ) + private static RandomSource impl$useCurrentWorld(final ServerLevel instance, final Operation original, final CommandSourceStack source) { + return original.call(source.getLevel()); + } + + /** + * Redirects weather parameter changes to the command source's current world + * instead of the global server weather. This enables per-world weather control + * via the {@code /weather} command. + * + *

The vanilla implementation calls {@link MinecraftServer#setWeatherParameters(int, int, boolean, boolean)} + * which sets weather on the global {@link WeatherData}. Since {@code ServerLevel} does not extend + * {@code MinecraftServer}, we cannot pass the level to {@code original.call()}. Instead, we + * directly manipulate the per-world {@link WeatherData} obtained from the source's level.

+ */ + @WrapOperation(method = { "setClear", "setRain", "setThunder" - }, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;overworld()Lnet/minecraft/server/level/ServerLevel;")) - private static ServerLevel impl$useCurrentWorld(final MinecraftServer instance, final CommandSourceStack $$0) { - return $$0.getLevel(); + }, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;setWeatherParameters(IIZZ)V")) + private static void impl$useCurrentWorld( + final MinecraftServer instance, final int clearTime, final int rainTime, final boolean raining, + final boolean thundering, final Operation original, + final CommandSourceStack source + ) { + final WeatherData weatherData = source.getLevel().getWeatherData(); + weatherData.setClearWeatherTime(clearTime); + weatherData.setRainTime(rainTime); + weatherData.setThunderTime(rainTime); + weatherData.setRaining(raining); + weatherData.setThundering(thundering); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedPlayerListMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedPlayerListMixin.java index 6a23e98a14a..631d228d7b3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedPlayerListMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedPlayerListMixin.java @@ -24,11 +24,12 @@ */ package org.spongepowered.common.mixin.core.server.dedicated; -import com.mojang.authlib.GameProfile; import net.minecraft.core.LayeredRegistryAccess; import net.minecraft.server.MinecraftServer; import net.minecraft.server.RegistryLayer; import net.minecraft.server.dedicated.DedicatedPlayerList; +import net.minecraft.server.notifications.EmptyNotificationService; +import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.PlayerList; import net.minecraft.world.level.storage.PlayerDataStorage; import org.spongepowered.api.Sponge; @@ -46,18 +47,18 @@ public abstract class DedicatedPlayerListMixin extends PlayerList { public DedicatedPlayerListMixin(final MinecraftServer server, final LayeredRegistryAccess registryAccess, - final PlayerDataStorage playerIo, final int maxPlayers) { - super(server, registryAccess, playerIo, maxPlayers); + final PlayerDataStorage playerIo) { + super(server, registryAccess, playerIo, new EmptyNotificationService()); } @Inject(method = "isWhiteListed", at = @At("HEAD"), cancellable = true) - private void impl$checkForWhitelistBypassPermission(final GameProfile profile, final CallbackInfoReturnable ci) { + private void impl$checkForWhitelistBypassPermission(final NameAndId profile, final CallbackInfoReturnable ci) { if (!this.isUsingWhitelist() || this.getWhiteList().isWhiteListed(profile)) { ci.setReturnValue(true); return; } final PermissionService permissionService = Sponge.server().serviceProvider().permissionService(); - Subject subject = permissionService.userSubjects().subject(profile.getId().toString()).orElse(null); + Subject subject = permissionService.userSubjects().subject(profile.id().toString()).orElse(null); if (subject == null) { subject = permissionService.userSubjects().defaults(); } @@ -66,9 +67,9 @@ public DedicatedPlayerListMixin(final MinecraftServer server, final LayeredRegis } @Inject(method = "canBypassPlayerLimit", at = @At("HEAD"), cancellable = true) - private void impl$checkForPlayerLimitBypassPermission(final GameProfile profile, final CallbackInfoReturnable ci) { + private void impl$checkForPlayerLimitBypassPermission(final NameAndId profile, final CallbackInfoReturnable ci) { final PermissionService permissionService = Sponge.server().serviceProvider().permissionService(); - Subject subject = permissionService.userSubjects().subject(profile.getId().toString()).orElse(null); + Subject subject = permissionService.userSubjects().subject(profile.id().toString()).orElse(null); if (subject == null) { subject = permissionService.userSubjects().defaults(); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedServerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedServerMixin.java index 758ddb4846b..6ce594f360e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedServerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedServerMixin.java @@ -33,9 +33,9 @@ import net.minecraft.server.WorldStem; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServerSettings; -import net.minecraft.server.level.progress.ChunkProgressListenerFactory; import net.minecraft.server.packs.repository.PackRepository; -import net.minecraft.server.players.GameProfileCache; +import net.minecraft.server.players.UserNameToIdResolver; +import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.storage.LevelStorageSource; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -44,7 +44,6 @@ import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.server.players.GameProfileCacheBridge; -import org.spongepowered.common.datapack.SpongeDataPackManager; import org.spongepowered.common.launch.Launch; import org.spongepowered.common.mixin.core.server.MinecraftServerMixin; @@ -56,23 +55,24 @@ public abstract class DedicatedServerMixin extends MinecraftServerMixin { @Inject(method = "", at = @At("TAIL")) - private void impl$setServerOnGame(Thread $$0, LevelStorageSource.LevelStorageAccess $$1, PackRepository $$2, WorldStem $$3, - DedicatedServerSettings $$4, DataFixer $$5, Services $$6, ChunkProgressListenerFactory $$7, CallbackInfo ci) { - $$4.getProperties().serverResourcePackInfo.ifPresent(packInfo -> { + private void impl$setServerOnGame( + final Thread $$0, final LevelStorageSource.LevelStorageAccess $$1, final PackRepository $$2, final WorldStem $$3, + final Optional $$4, final DedicatedServerSettings $$5, final DataFixer $$6, final Services $$7, + final net.minecraft.server.jsonrpc.ManagementServer $$8, final net.minecraft.server.notifications.NotificationManager $$9, + final CallbackInfo ci + ) { + $$5.getProperties().serverResourcePackInfo.ifPresent(packInfo -> { try { this.impl$resourcePack = ResourcePackRequest.resourcePackRequest() - .packs(ResourcePackInfo.resourcePackInfo(packInfo.id(), new URI(packInfo.url()), packInfo.hash())) - .required(packInfo.isRequired()) - .prompt(SpongeAdventure.asAdventure(Optional.ofNullable(packInfo.prompt()))) - .build(); + .packs(ResourcePackInfo.resourcePackInfo(packInfo.id(), new URI(packInfo.url()), packInfo.hash())) + .required(packInfo.isRequired()) + .prompt(SpongeAdventure.asAdventure(Optional.ofNullable(packInfo.prompt()))) + .build(); } catch (final URISyntaxException e) { e.printStackTrace(); } }); SpongeCommon.game().setServer(this); - $$6.profileCache().load(); - - ((SpongeDataPackManager) this.dataPackManager()).init(); } @Override @@ -80,8 +80,8 @@ public abstract class DedicatedServerMixin extends MinecraftServerMixin { return this.shadow$isRunning(); } - @WrapOperation(method = "initServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/GameProfileCache;save()V")) - private void onSave(final GameProfileCache cache, final Operation original) { + @WrapOperation(method = "initServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/UserNameToIdResolver;save()V")) + private void onSave(final UserNameToIdResolver cache, final Operation original) { ((GameProfileCacheBridge) cache).bridge$setCanSave(true); original.call(cache); ((GameProfileCacheBridge) cache).bridge$setCanSave(false); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkHolderMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkHolderMixin.java index 2e2d9b36ab0..cec898b678c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkHolderMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkHolderMixin.java @@ -68,7 +68,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolderMixin implements Ch /** * After onFullChunkStatusChange */ - @Inject(method = "lambda$scheduleFullChunkPromotion$4", at = @At("TAIL")) + @Inject(method = "lambda$scheduleFullChunkPromotion$0", at = @At("TAIL")) private void impl$onScheduleFullChunkPromotion(final ChunkMap $$0x, final FullChunkStatus $$1x, final CallbackInfo ci) { if ($$1x == FullChunkStatus.ENTITY_TICKING) { this.shadow$getEntityTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).ifSuccess(chunk -> { @@ -76,7 +76,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolderMixin implements Ch if (ShouldFire.CHUNK_EVENT_LOAD) { final Vector3i chunkPos = VecHelper.toVector3i(chunk.getPos()); final ChunkEvent.Load event = SpongeEventFactory.createChunkEventLoad(PhaseTracker.getInstance().currentCause(), - (WorldChunk) chunk, chunkPos, (ResourceKey) (Object) chunk.getLevel().dimension().location()); + (WorldChunk) chunk, chunkPos, (ResourceKey) (Object) chunk.getLevel().dimension().identifier()); SpongeCommon.post(event); } }); @@ -88,7 +88,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolderMixin implements Ch if (this.impl$loadedChunk != null && ShouldFire.CHUNK_EVENT_UNLOAD_PRE) { final Vector3i chunkPos = VecHelper.toVector3i(this.impl$loadedChunk.getPos()); final ChunkEvent.Unload event = SpongeEventFactory.createChunkEventUnloadPre(PhaseTracker.getInstance().currentCause(), - (WorldChunk) this.impl$loadedChunk, chunkPos, (ResourceKey) (Object) this.impl$loadedChunk.getLevel().dimension().location()); + (WorldChunk) this.impl$loadedChunk, chunkPos, (ResourceKey) (Object) this.impl$loadedChunk.getLevel().dimension().identifier()); SpongeCommon.post(event); } } @@ -98,7 +98,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolderMixin implements Ch if (this.impl$loadedChunk != null && ShouldFire.CHUNK_EVENT_UNLOAD_POST) { final Vector3i chunkPos = VecHelper.toVector3i(this.impl$loadedChunk.getPos()); final ChunkEvent.Unload event = SpongeEventFactory.createChunkEventUnloadPost(PhaseTracker.getInstance().currentCause(), chunkPos, - (ResourceKey) (Object) this.impl$loadedChunk.getLevel().dimension().location()); + (ResourceKey) (Object) this.impl$loadedChunk.getLevel().dimension().identifier()); SpongeCommon.post(event); } this.impl$loadedChunk = null; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMapMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMapMixin.java index e9d2895e218..8230ce5ed22 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMapMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMapMixin.java @@ -31,7 +31,6 @@ import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.storage.ChunkSerializer; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.world.chunk.ChunkEvent; @@ -50,7 +49,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.accessor.server.level.ServerChunkCacheAccessor; -import org.spongepowered.common.accessor.world.level.chunk.storage.ChunkStorageAccessor; +import org.spongepowered.common.accessor.world.level.chunk.storage.SimpleRegionStorageAccessor; import org.spongepowered.common.bridge.server.level.ServerLevelBridge; import org.spongepowered.common.bridge.world.DistanceManagerBridge; import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge; @@ -67,6 +66,7 @@ import org.spongepowered.math.vector.Vector3i; import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; @Mixin(ChunkMap.class) public abstract class ChunkMapMixin implements ChunkMapBridge { @@ -84,7 +84,7 @@ public abstract class ChunkMapMixin implements ChunkMapBridge { @Inject(method = "", at = @At("RETURN")) private void impl$setIOWorkerDimension(final CallbackInfo ci) { - ((IOWorkerBridge) ((ChunkStorageAccessor) this).accessor$worker()).bridge$setDimension(SpongeIOWorkerType.CHUNK, this.level.dimension()); + ((IOWorkerBridge) ((SimpleRegionStorageAccessor) this).accessor$worker()).bridge$setDimension(SpongeIOWorkerType.CHUNK, this.level.dimension()); } @Redirect(method = "save", @@ -98,29 +98,18 @@ public abstract class ChunkMapMixin implements ChunkMapBridge { } @Redirect(method = "save", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/chunk/storage/ChunkSerializer;write(Lnet/minecraft/server/level/ServerLevel;" - + "Lnet/minecraft/world/level/chunk/ChunkAccess;)Lnet/minecraft/nbt/CompoundTag;")) - private CompoundTag impl$useSerializationBehaviorForChunkSave(final ServerLevel worldIn, final ChunkAccess chunkIn) { + target = "Lnet/minecraft/server/level/ChunkMap;write(Lnet/minecraft/world/level/ChunkPos;Ljava/util/function/Supplier;)Ljava/util/concurrent/CompletableFuture;")) + private CompletableFuture impl$doNotWriteIfWeHaveNoData(final ChunkMap chunkManager, final ChunkPos pos, final Supplier compound) { final ServerLevelDataBridge infoBridge = (ServerLevelDataBridge) this.level.getLevelData(); final SerializationBehavior serializationBehavior = infoBridge.bridge$serializationBehavior().orElse(SerializationBehavior.AUTOMATIC); if (serializationBehavior == SerializationBehavior.AUTOMATIC || serializationBehavior == SerializationBehavior.MANUAL) { - return ChunkSerializer.write(worldIn, chunkIn); + return chunkManager.write(pos, compound); } - return null; + return CompletableFuture.completedFuture(null); } - @Redirect(method = "save", at = @At(value = "INVOKE", - target = "Lnet/minecraft/server/level/ChunkMap;write(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/nbt/CompoundTag;)Ljava/util/concurrent/CompletableFuture;")) - private CompletableFuture impl$doNotWriteIfWeHaveNoData(final ChunkMap chunkManager, final ChunkPos pos, final CompoundTag compound) { - if (compound == null) { - return CompletableFuture.completedFuture(null); - } - - return chunkManager.write(pos, compound); - } - - @Redirect(method = "lambda$scheduleUnload$12", + @Redirect(method = "lambda$scheduleUnload$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;unload(Lnet/minecraft/world/level/chunk/LevelChunk;)V"), slice = @Slice( from = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ChunkMap;save(Lnet/minecraft/world/level/chunk/ChunkAccess;)Z") @@ -146,7 +135,7 @@ public abstract class ChunkMapMixin implements ChunkMapBridge { if (ShouldFire.CHUNK_EVENT_BLOCKS_SAVE_PRE) { final Vector3i chunkPos = VecHelper.toVector3i(var1.getPos()); final ChunkEvent.Blocks.Save.Pre postSave = SpongeEventFactory.createChunkEventBlocksSavePre(PhaseTracker.getInstance().currentCause(), - ((BlockChunk) var1), chunkPos, (ResourceKey) (Object) this.level.dimension().location()); + ((BlockChunk) var1), chunkPos, (ResourceKey) (Object) this.level.dimension().identifier()); SpongeCommon.post(postSave); if (postSave.isCancelled()) { cir.setReturnValue(false); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java index a3a9a2482d4..87c7b16da99 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ChunkMap_TrackedEntityMixin.java @@ -28,6 +28,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.server.network.ServerPlayerConnection; @@ -57,12 +58,12 @@ public abstract class ChunkMap_TrackedEntityMixin { * 2) We already have the players being iterated * 3) This achieves the same functionality without adding new accessors etc. */ - @Inject(method = "broadcast(Lnet/minecraft/network/protocol/Packet;)V", at = @At(value = "INVOKE", shift = At.Shift.AFTER, + @Inject(method = "sendToTrackingPlayers(Lnet/minecraft/network/protocol/Packet;)V", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/server/network/ServerPlayerConnection;send(Lnet/minecraft/network/protocol/Packet;)V")) private void impl$sendQueuedHumanPackets(final CallbackInfo ci, @Local final ServerPlayerConnection connection) { if (this.entity instanceof HumanEntity && connection instanceof ServerGamePacketListenerImpl) { final ServerPlayer player = ((ServerGamePacketListenerImpl) connection).player; - final Stream> packets = ((HumanEntity) this.entity).popQueuedPackets(player); + final Stream> packets = ((HumanEntity) this.entity).popQueuedPackets(player); packets.forEach(player.connection::send); } } @@ -74,7 +75,7 @@ public abstract class ChunkMap_TrackedEntityMixin { * entity is being "removed" from clients by way of literally being mimiced being * "untracked". This safeguards the players being updated erroneously. */ - @Inject(method = "broadcast(Lnet/minecraft/network/protocol/Packet;)V", at = @At("HEAD"), cancellable = true) + @Inject(method = "sendToTrackingPlayers(Lnet/minecraft/network/protocol/Packet;)V", at = @At("HEAD"), cancellable = true) private void impl$ignoreVanished(final Packet packet, final CallbackInfo ci) { if (this.entity instanceof VanishableBridge) { if (((VanishableBridge) this.entity).bridge$vanishState().invisible()) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/DistanceManagerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/DistanceManagerMixin.java index 99184b1c0e5..512e317baa7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/DistanceManagerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/DistanceManagerMixin.java @@ -24,23 +24,18 @@ */ package org.spongepowered.common.mixin.core.server.level; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minecraft.server.level.DistanceManager; import net.minecraft.server.level.TicketType; import net.minecraft.util.Mth; -import net.minecraft.util.SortedArraySet; -import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.TicketStorage; import org.spongepowered.api.util.Ticks; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.api.world.server.Ticket; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.accessor.server.level.TicketAccessor; +import org.spongepowered.common.accessor.world.level.TicketStorageAccessor; import org.spongepowered.common.bridge.world.DistanceManagerBridge; import org.spongepowered.common.bridge.world.server.TicketBridge; import org.spongepowered.common.util.Constants; @@ -56,20 +51,18 @@ public abstract class DistanceManagerMixin implements DistanceManagerBridge { // @formatter:off - @Shadow private void shadow$addTicket(final long chunkpos, final net.minecraft.server.level.Ticket ticket) { } - @Shadow protected abstract void shadow$removeTicket(long chunkPosIn, net.minecraft.server.level.Ticket ticketIn); - @Shadow @Final private Long2ObjectOpenHashMap>> tickets; - @Shadow private long ticketTickCounter; + @Shadow @Final TicketStorage ticketStorage; // @formatter:on @Override @SuppressWarnings({"unchecked", "ConstantConditions"}) public boolean bridge$checkTicketValid(final Ticket ticket) { // Only report the ticket is valid if it's associated with this manager. - final net.minecraft.server.level.Ticket nativeTicket = ((net.minecraft.server.level.Ticket) (Object) ticket); - final SortedArraySet> ticketsForChunk = this.tickets.get(((TicketBridge) ticket).bridge$chunkPosition()); - if (ticketsForChunk != null && ticketsForChunk.contains(nativeTicket)) { - return !((TicketAccessor) ticket).invoker$timedOut(this.ticketTickCounter); + final var nativeTicket = ((net.minecraft.server.level.Ticket) (Object) ticket); + final var bridgeTicket = (TicketBridge) ticket; + final var tickets = this.ticketStorage.getTickets(bridgeTicket.bridge$chunkPosition()); + if (tickets != null && tickets.contains(nativeTicket)) { + return !nativeTicket.isTimedOut(); } return false; } @@ -78,8 +71,12 @@ public abstract class DistanceManagerMixin implements DistanceManagerBridge { @SuppressWarnings("ConstantConditions") public Ticks bridge$timeLeft(final Ticket ticket) { if (this.bridge$checkTicketValid(ticket)) { - final long ticksElapsed = this.ticketTickCounter - ((TicketAccessor) ticket).accessor$createdTick(); - return new SpongeTicks(Math.max(0, ((net.minecraft.server.level.Ticket) (Object) ticket).getType().timeout() - ticksElapsed)); + final var mcTicket = (net.minecraft.server.level.Ticket) ticket; + if (mcTicket.getType().timeout() == 0) { + return Ticks.zero(); + } + final long left = ((TicketAccessor) ticket).accessor$ticksLeft(); + return new SpongeTicks(Math.max(0, left)); } return Ticks.zero(); } @@ -88,8 +85,9 @@ public abstract class DistanceManagerMixin implements DistanceManagerBridge { @SuppressWarnings({"unchecked", "ConstantConditions"}) public boolean bridge$renewTicket(final Ticket ticket) { if (this.bridge$checkTicketValid(ticket)) { - final net.minecraft.server.level.Ticket nativeTicket = (net.minecraft.server.level.Ticket) (Object) ticket; - ((TicketAccessor) ticket).invoker$setCreatedTick(this.ticketTickCounter + nativeTicket.getType().timeout()); + final var nativeTicket = (net.minecraft.server.level.Ticket) (Object) ticket; + nativeTicket.resetTicksLeft(); + this.ticketStorage.setDirty(); return true; } return false; @@ -101,9 +99,9 @@ public abstract class DistanceManagerMixin implements DistanceManagerBridge { final ServerWorld world, final org.spongepowered.api.world.server.TicketType ticketType, final Vector3i pos, final T value, final int distanceLimit) { final int distance = Mth.clamp(Constants.ChunkTicket.MAX_FULL_CHUNK_DISTANCE - distanceLimit, 0, Constants.ChunkTicket.MAX_FULL_CHUNK_TICKET_LEVEL); - final TicketType type = (net.minecraft.server.level.TicketType) ticketType; - final net.minecraft.server.level.Ticket ticketToRequest = TicketAccessor.accessor$createInstance(type, distance, value); - this.shadow$addTicket(VecHelper.toChunkPos(pos).toLong(), ticketToRequest); + final TicketType type = (TicketType) (Object) ticketType; + final net.minecraft.server.level.Ticket ticketToRequest = new net.minecraft.server.level.Ticket(type, distance); + this.ticketStorage.addTicket(VecHelper.toChunkPos(pos).pack(), ticketToRequest); return Optional.of(((TicketBridge) (Object) ticketToRequest).bridge$retrieveAppropriateTicket()); } @@ -111,9 +109,8 @@ public abstract class DistanceManagerMixin implements DistanceManagerBridge { @SuppressWarnings({"ConstantConditions"}) public boolean bridge$releaseTicket(final Ticket ticket) { if (this.bridge$checkTicketValid(ticket)) { - this.shadow$removeTicket(((TicketBridge) ticket).bridge$chunkPosition(), - (net.minecraft.server.level.Ticket) (Object) ticket); - return true; + final var chunkPos = ((TicketBridge) ticket).bridge$chunkPosition(); + return this.ticketStorage.removeTicket(chunkPos, (net.minecraft.server.level.Ticket) ticket); } return false; } @@ -121,32 +118,10 @@ public abstract class DistanceManagerMixin implements DistanceManagerBridge { @SuppressWarnings({"ConstantConditions", "unchecked"}) @Override public Collection> bridge$tickets(final org.spongepowered.api.world.server.TicketType ticketType) { - return this.tickets.values().stream() - .flatMap(x -> x.stream().filter(ticket -> ticket.getType() == ticketType)) + return ((TicketStorageAccessor) this.ticketStorage).accessor$tickets().values().stream() + .flatMap(x -> x.stream().filter(ticket -> ticket.getType() == (TicketType) (Object) ticketType)) .map(x -> (Ticket) (Object) x) .collect(Collectors.toList()); } - @SuppressWarnings("ConstantConditions") - @Inject(method = "addTicket(JLnet/minecraft/server/level/Ticket;)V", at = @At("HEAD")) - private void impl$addChunkPosToTicket(final long chunkPos, final net.minecraft.server.level.Ticket ticket, final CallbackInfo ci) { - ((TicketBridge) (Object) ticket).bridge$setChunkPosition(chunkPos); - } - - @SuppressWarnings("ConstantConditions") - @Redirect(method = "addTicket(JLnet/minecraft/server/level/Ticket;)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/Ticket;setCreatedTick(J)V")) - private void impl$setParentTicketIfApplicable(final net.minecraft.server.level.Ticket storedTicket, - final long ticketTickCounter, - final long chunkPosAsLong, - final net.minecraft.server.level.Ticket originalTicket) { - // We do this because we want to return the original ticket that will actually be operated on, but avoid a - // potentially costly search on an array - because addTicket doesn't return the ticket that is actually - // in the manager. - if (storedTicket != originalTicket) { - ((TicketBridge) (Object) originalTicket).bridge$setParentTicket(storedTicket); - } - ((TicketAccessor) (Object) storedTicket).invoker$setCreatedTick(ticketTickCounter); - } - } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/GenerationChunkHolderMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/GenerationChunkHolderMixin.java index c911e45bb3c..cb3651e15bf 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/GenerationChunkHolderMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/GenerationChunkHolderMixin.java @@ -81,7 +81,7 @@ public abstract class GenerationChunkHolderMixin { final Vector3i chunkPos = VecHelper.toVector3i(chunk.getPos()); final ChunkEvent.Generated event = SpongeEventFactory.createChunkEventGenerated( PhaseTracker.getInstance().currentCause(), chunkPos, - (ResourceKey) (Object) chunk.getLevel().dimension().location() + (ResourceKey) (Object) chunk.getLevel().dimension().identifier() ); SpongeCommon.post(event); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/PlayerSpawnFinderMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/PlayerSpawnFinderMixin.java new file mode 100644 index 00000000000..10493823487 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/PlayerSpawnFinderMixin.java @@ -0,0 +1,139 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.server.level; + +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.PlayerSpawnFinder; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.TicketType; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.CollisionGetter; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; + +import javax.annotation.Nullable; + +@Mixin(PlayerSpawnFinder.class) +public abstract class PlayerSpawnFinderMixin { + + @Shadow @Final private CompletableFuture finishedFuture; + @Shadow @Final private ServerLevel level; + @Shadow @Final private BlockPos spawnSuggestion; + @Shadow @Final private int radius; + @Shadow @Final private int candidateCount; + @Shadow private int nextCandidateIndex; + @Shadow @Final private int offset; + @Shadow @Final private int coprime; + + @Shadow private static Vec3 fixupSpawnHeight(CollisionGetter level, BlockPos pos) { return null; } + @Shadow @Nullable protected static BlockPos getLevelRespawnPos(ServerLevel level, int x, int z) { return null; } + @Shadow private static boolean noCollisionNoLiquid(CollisionGetter level, BlockPos pos) { return false; } + + /** + * A mix of loop and recursion. + * Loop is preferred when possible. + * + * @author Yeregorix + * @reason Fix
MC-304426. + */ + @Overwrite + private void scheduleNext() { + int i; + while ((i = this.nextCandidateIndex++) < this.candidateCount) { + if (this.finishedFuture.isDone()) { + return; + } + + final int candidate = (this.offset + this.coprime * i) % this.candidateCount; + final int relX = candidate % (this.radius * 2 + 1); + final int relZ = candidate / (this.radius * 2 + 1); + final int x = this.spawnSuggestion.getX() + relX - this.radius; + final int z = this.spawnSuggestion.getZ() + relZ - this.radius; + + if (this.scheduleCandidate(x, z, i, () -> { + final BlockPos pos = getLevelRespawnPos(this.level, x, z); + return pos != null && noCollisionNoLiquid(this.level, pos) ? Optional.of(Vec3.atBottomCenterOf(pos)) : Optional.empty(); + })) { + return; + } + } + + if (this.finishedFuture.isDone()) { + return; + } + + scheduleCandidate(this.spawnSuggestion.getX(), this.spawnSuggestion.getZ(), i, () -> Optional.of(fixupSpawnHeight(this.level, this.spawnSuggestion))); + } + + private boolean scheduleCandidate(final int x, final int z, final int index, final Supplier> supplier) { + final int chunkX = SectionPos.blockToSectionCoord(x); + final int chunkZ = SectionPos.blockToSectionCoord(z); + final CompletableFuture future = this.level.getChunkSource().addTicketAndLoadWithRadius(TicketType.SPAWN_SEARCH, new ChunkPos(chunkX, chunkZ), 0); + final MinecraftServer server = this.level.getServer(); + + // We exit either the loop or the recursion, depending on what happens first. + final AtomicBoolean exit = new AtomicBoolean(true); + + future.whenCompleteAsync((chunk, error) -> { + if (error == null) { + try { + Optional result = supplier.get(); + if (result.isPresent()) { + this.finishedFuture.complete(result.get()); + } else if (!exit.getAndSet(false)) { + this.scheduleNext(); + } + } catch (Exception exception) { + error = exception; + } + } + + if (error != null) { + CrashReport report = CrashReport.forThrowable(error, "Searching for spawn"); + CrashReportCategory category = report.addCategory("Spawn Lookup"); + category.setDetail("Origin", this.spawnSuggestion::toString); + category.setDetail("Radius", () -> Integer.toString(this.radius)); + category.setDetail("Candidate", () -> "[" + x + "," + z + "]"); + category.setDetail("Progress", () -> index + " out of " + this.candidateCount); + this.finishedFuture.completeExceptionally(new ReportedException(report)); + } + }, server); + + return exit.getAndSet(false); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerChunkCacheMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerChunkCacheMixin.java index 557fd6d0819..8abfcb9aef1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerChunkCacheMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerChunkCacheMixin.java @@ -61,7 +61,7 @@ public abstract class ServerChunkCacheMixin extends ChunkSourceMixin { @Override public void bridge$biomeChanged(final ChunkPos pos) { - final @Nullable ChunkHolder holder = this.shadow$getVisibleChunkIfPresent(pos.toLong()); + final @Nullable ChunkHolder holder = this.shadow$getVisibleChunkIfPresent(pos.pack()); if (holder != null) { ((ChunkHolderBridge) holder).bridge$markBiomesDirty(); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java index bd056f1fa56..2a56f356b46 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin.java @@ -24,8 +24,12 @@ */ package org.spongepowered.common.mixin.core.server.level; -import com.llamalad7.mixinextras.injector.ModifyReceiver; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; +import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerEntity; import net.minecraft.server.level.ServerLevel; @@ -40,16 +44,18 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.accessor.world.entity.LivingEntityAccessor; -import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.server.level.ServerPlayerBridge; +import org.spongepowered.common.data.datasync.VanishedFilteringSynchronizer; import org.spongepowered.common.entity.living.human.HumanEntity; +import java.lang.ref.WeakReference; import java.util.Collection; +import java.util.EnumSet; import java.util.List; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.stream.Stream; @Mixin(ServerEntity.class) @@ -57,12 +63,16 @@ public abstract class ServerEntityMixin { // @formatter:off @Shadow @Final private Entity entity; - @Shadow @Final @Mutable private Consumer> broadcast; - - @Shadow protected abstract void shadow$broadcastAndSend(Packet arg); + @Shadow @Final @Mutable private ServerEntity.Synchronizer synchronizer; // @formatter:on /** + * @param serverLevel The world + * @param entity The entity being tracked + * @param trackingRange The update frequency + * @param trackMovementDeltas Whether velocity updates are sent + * @param broadcaster The consumer (a method handle for EntityTracker#sendToAllTracking) + * @param ci The callback info * @author gabizou * @reason Because the packets for *most* all entity updates are handled * through this consumer tick, basically all the players tracking the @@ -75,24 +85,13 @@ public abstract class ServerEntityMixin { * any and all players tracking our tracked entity by filtering the consumer first, * then as a fail safe, the EntityTracker mixin. Meanwhile, all other states are updated * just fine. - * - * @param serverLevel The world - * @param entity The entity being tracked - * @param trackingRange The update frequency - * @param trackMovementDeltas Whether velocity updates are sent - * @param broadcaster The consumer (a method handle for EntityTracker#sendToAllTracking) - * @param ci The callback info */ @Inject(method = "", at = @At("TAIL")) - private void impl$wrapConsumer(final ServerLevel serverLevel, final Entity entity, final int trackingRange, - final boolean trackMovementDeltas, final Consumer> broadcaster, final CallbackInfo ci) { - this.broadcast = (packet) -> { - if (this.entity instanceof VanishableBridge) { - if (!((VanishableBridge) this.entity).bridge$vanishState().invisible()) { - broadcaster.accept(packet); - } - } - }; + private void impl$wrapConsumer( + final ServerLevel serverLevel, final Entity entity, final int trackingRange, + final boolean trackMovementDeltas, final ServerEntity.Synchronizer broadcaster, + final CallbackInfo ci) { + this.synchronizer = new VanishedFilteringSynchronizer(broadcaster, new WeakReference<>(this.entity)); } @Inject(method = "removePairing", at = @At("RETURN")) @@ -102,13 +101,42 @@ public abstract class ServerEntityMixin { } } + /** + * @author gabizou + * @reason Because the entity spawn packet is just a lone packet, we have to actually + * do some hackery to create the player list packet first, then the spawn packet, + * then perform the remove packet. + */ + @Redirect( + method = "sendPairingData", + at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V", ordinal = 0) + ) + public void impl$sendHumanSpawnPacket(final Consumer> consumer, final Object spawnPacket) { + if (!(this.entity instanceof final HumanEntity human)) { + consumer.accept((Packet) spawnPacket); + return; + } + // Adds the GameProfile to the client + consumer.accept(human.createPlayerListPacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER))); + // Actually spawn the human (a player) + consumer.accept((Packet) spawnPacket); + // Remove from the player map + final ClientboundPlayerInfoRemovePacket removePacket = new ClientboundPlayerInfoRemovePacket(List.of(human.getUUID())); + if (human.canRemoveFromListImmediately()) { + consumer.accept(removePacket); + } else { + // Human is a Player entity on the client and needs to tick once for the skin to render + human.removeFromTabListDelayed(null, removePacket); + } + } + @Inject(method = "sendDirtyEntityData", at = @At("HEAD")) public void impl$sendHumanMetadata(final CallbackInfo ci) { if (!(this.entity instanceof final HumanEntity human)) { return; } - final Stream> packets = human.popQueuedPackets(null); - packets.forEach(this.broadcast); + final Stream> packets = human.popQueuedPackets(null); + packets.forEach(this.synchronizer::sendToTrackingPlayers); // Note that this will further call in ChunkManager_EntityTrackerMixin // for any player specific packets to send. } @@ -137,16 +165,19 @@ public abstract class ServerEntityMixin { return packed; } - @ModifyReceiver(method = "sendChanges", at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V"), - slice = @Slice( - from = @At(value = "NEW", target = "(Lnet/minecraft/world/entity/Entity;)Lnet/minecraft/network/protocol/game/ClientboundSetPassengersPacket;"), - to = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerEntity;removedPassengers(Ljava/util/List;Ljava/util/List;)Ljava/util/stream/Stream;"))) - private Consumer> impl$sendSetPassengersToSelf(final Consumer> instance, final T packet) { + @WrapOperation(method = "sendChanges", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/server/level/ServerEntity$Synchronizer;sendToTrackingPlayersFiltered(Lnet/minecraft/network/protocol/Packet;Ljava/util/function/Predicate;)V")) + private void impl$sendSetPassengersToSelf( + final ServerEntity.Synchronizer instance, final Packet packet, + final Predicate serverPlayerPredicate, final Operation original) { // When passengers are removed from a player entity, the target // is the player itself, and we need to synchronize it to them. // In vanilla, it is not possible to ride player entities // so we never end up hitting this code path. - // noinspection Convert2MethodRef https://github.com/SpongePowered/Mixin/issues/406 - return p -> this.shadow$broadcastAndSend(p); + original.call(instance, packet, serverPlayerPredicate); + if (this.entity instanceof final ServerPlayer player) { + player.connection.send(packet); + } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin_Shared.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin_Shared.java deleted file mode 100644 index c7785b5bfa7..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerEntityMixin_Shared.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.server.level; - -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; -import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; -import net.minecraft.server.level.ServerEntity; -import net.minecraft.world.entity.Entity; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.entity.living.human.HumanEntity; - -import java.util.EnumSet; -import java.util.List; -import java.util.function.Consumer; - -// Forge and Vanilla -@Mixin(ServerEntity.class) -public class ServerEntityMixin_Shared { - @Shadow @Final private Entity entity; - - /** - * @author gabizou - * @reason Because the entity spawn packet is just a lone packet, we have to actually - * do some hackery to create the player list packet first, then the spawn packet, - * then perform the remove packet. - */ - @Redirect( - method = "sendPairingData", - at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V", ordinal = 0) - ) - public void impl$sendHumanSpawnPacket(final Consumer> consumer, final Object spawnPacket) { - if (!(this.entity instanceof final HumanEntity human)) { - consumer.accept((Packet) spawnPacket); - return; - } - // Adds the GameProfile to the client - consumer.accept(human.createPlayerListPacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER))); - // Actually spawn the human (a player) - consumer.accept((Packet) spawnPacket); - // Remove from the player map - final ClientboundPlayerInfoRemovePacket removePacket = new ClientboundPlayerInfoRemovePacket(List.of(human.getUUID())); - if (human.canRemoveFromListImmediately()) { - consumer.accept(removePacket); - } else { - // Human is a Player entity on the client and needs to tick once for the skin to render - human.removeFromTabListDelayed(null, removePacket); - } - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerLevelMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerLevelMixin.java index a89bad69003..283babba206 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerLevelMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerLevelMixin.java @@ -29,39 +29,61 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; -import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; +import net.minecraft.core.particles.BlockParticleOption; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.registries.Registries; -import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientboundExplodePacket; +import net.minecraft.resources.Identifier; import net.minecraft.server.MinecraftServer; import net.minecraft.server.bossevents.CustomBossEvents; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.progress.ChunkProgressListener; -import net.minecraft.server.players.PlayerList; -import net.minecraft.world.RandomSequences; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.util.Util; +import net.minecraft.util.random.WeightedList; +import net.minecraft.world.DifficultyInstance; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.LightningBolt; +import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.village.poi.PoiManager; +import net.minecraft.world.entity.ai.village.poi.PoiRecord; import net.minecraft.world.entity.ai.village.poi.PoiType; -import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.decoration.HangingEntity; +import net.minecraft.world.entity.decoration.painting.Painting; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownEnderpearl; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.level.CustomSpawner; -import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; +import net.minecraft.world.level.ServerExplosion; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.JukeboxBlockEntity; import net.minecraft.world.level.block.entity.TickingBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.dimension.end.EndDragonFight; +import net.minecraft.world.level.dimension.end.EnderDragonFight; import net.minecraft.world.level.entity.PersistentEntitySectionManager; import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.gamerules.GameRuleMap; +import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.storage.DimensionDataStorage; +import net.minecraft.world.level.saveddata.WeatherData; import net.minecraft.world.level.storage.LevelData; import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.SavedDataStorage; import net.minecraft.world.level.storage.ServerLevelData; import net.minecraft.world.level.storage.WorldData; import net.minecraft.world.phys.Vec3; @@ -75,6 +97,8 @@ import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.Transaction; import org.spongepowered.api.effect.sound.music.MusicDisc; +import org.spongepowered.api.entity.EntityType; +import org.spongepowered.api.entity.projectile.EnderPearl; import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; @@ -92,12 +116,16 @@ import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Coerce; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.accessor.world.entity.MobAccessor; +import org.spongepowered.common.accessor.world.entity.item.FallingBlockEntityAccessor; import org.spongepowered.common.block.SpongeBlockSnapshot; import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.server.level.ServerLevelBridge; @@ -106,9 +134,11 @@ import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge; import org.spongepowered.common.bridge.world.level.dimension.DimensionTypeBridge; import org.spongepowered.common.bridge.world.level.storage.DimensionDataStorageBridge; +import org.spongepowered.common.bridge.world.level.storage.PrimaryLevelDataBridge; import org.spongepowered.common.bridge.world.level.storage.ServerLevelDataBridge; import org.spongepowered.common.bridge.world.ticks.LevelTicksBridge; import org.spongepowered.common.config.SpongeGameConfigs; +import org.spongepowered.common.entity.projectile.UnknownProjectileSource; import org.spongepowered.common.event.ShouldFire; import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.tracking.PhaseContext; @@ -119,7 +149,10 @@ import org.spongepowered.common.mixin.core.world.level.LevelMixin; import org.spongepowered.common.util.Constants; import org.spongepowered.common.util.SpongeTicks; +import org.spongepowered.common.world.server.SpongeRegistryData; import org.spongepowered.common.world.server.SpongeServerLevelData; +import org.spongepowered.common.world.weather.SpongeWeather; +import org.spongepowered.math.vector.Vector3d; import org.spongepowered.math.vector.Vector3i; import java.util.List; @@ -141,9 +174,12 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel @Shadow @NonNull public abstract MinecraftServer shadow$getServer(); @Shadow @Final private MinecraftServer server; - @Shadow public abstract void levelEvent(@Nullable Player $$0, int $$1, BlockPos $$2, int $$3); - @Shadow @Nullable private EndDragonFight dragonFight; - @Shadow public abstract DimensionDataStorage shadow$getDataStorage(); + @Shadow @Nullable private EnderDragonFight dragonFight; + @Shadow @Final List players; + @Shadow public abstract SavedDataStorage shadow$getDataStorage(); + + @Shadow public abstract DifficultyInstance shadow$getCurrentDifficultyAt(BlockPos p_175649_1_); + @Shadow public abstract WeatherData shadow$getWeatherData(); // @formatter:on @@ -151,22 +187,96 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel private LevelStorageSource.LevelStorageAccess impl$levelSave; private CustomBossEvents impl$bossBarManager; - private ChunkProgressListener impl$chunkProgressListener; private Weather impl$prevWeather; private boolean impl$isManualSave = false; private long impl$preTickTime = 0L; + @Unique private @Nullable GameRules impl$gameRules; + + /** + * Initializes per-world {@link WeatherData} via vanilla's {@link SavedDataStorage}. + * This intercepts the {@code server.getWeatherData()} call used by {@code prepareWeather} + * in the constructor, ensuring each world has independent weather state from creation. + * + *

Since each dimension has its own {@link SavedDataStorage} (created by + * {@link ServerChunkCache} at {@code getDimensionPath(dim).resolve("data")}), + * {@code computeIfAbsent(WeatherData.TYPE)} creates a per-dimension {@code weather.dat} + * file automatically.

+ */ + @WrapOperation( + method = "", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getWeatherData()Lnet/minecraft/world/level/saveddata/WeatherData;") + ) + private WeatherData impl$initPerWorldWeatherData(final MinecraftServer instance, final Operation original) { + return this.shadow$getDataStorage().computeIfAbsent(WeatherData.TYPE); + } + + /** + * Redirects {@link ServerLevel#getWeatherData()} to return the per-world {@link WeatherData} + * from this dimension's {@link SavedDataStorage} instead of the global server weather data. + * This enables independent weather cycles, commands, and API access for each world. + */ + @WrapOperation( + method = "getWeatherData", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getWeatherData()Lnet/minecraft/world/level/saveddata/WeatherData;") + ) + private WeatherData impl$usePerWorldWeatherData(final MinecraftServer instance, final Operation original) { + return this.shadow$getDataStorage().computeIfAbsent(WeatherData.TYPE); + } + + /** + * Overrides {@link ServerLevel#getGameRules()} to return per-dimension game rules + * backed by a per-dimension {@link GameRuleMap} stored via {@link SavedDataStorage}. + * + *

The per-dimension {@code GameRuleMap} is loaded (or created) via + * {@code computeIfAbsent(GameRuleMap.TYPE)}, which stores/reads from + * each dimension's own {@code data/} directory as {@code game_rules.dat}.

+ */ + @WrapOperation( + method = "getGameRules", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getGameRules()Lnet/minecraft/world/level/gamerules/GameRules;") + ) + private GameRules impl$perWorldGameRules(final MinecraftServer instance, final Operation original) { + if (this.impl$gameRules == null) { + final GameRuleMap perWorldMap = this.shadow$getDataStorage().computeIfAbsent(GameRuleMap.TYPE); + final boolean isNewWorld = perWorldMap.size() == 0; + this.impl$gameRules = new GameRules( + instance.getWorldData().enabledFeatures(), + perWorldMap + ); + if (isNewWorld) { + // New world — copy the server's current game rule values as initial defaults + this.impl$gameRules.setAll(original.call(instance), null); + } + } + return this.impl$gameRules; + } @Inject(method = "", at = @At("TAIL")) private void impl$onInit( - final MinecraftServer server, final Executor executor, final LevelStorageSource.LevelStorageAccess storage, final ServerLevelData levelData, - final net.minecraft.resources.ResourceKey key, final LevelStem levelStem, final ChunkProgressListener listener, final boolean debug, final long biomeSeed, - final List spawners, final boolean tick, final RandomSequences randomSequences, final CallbackInfo ci + final MinecraftServer server, + final Executor executor, + final LevelStorageSource.LevelStorageAccess storage, + final ServerLevelData levelData, + final net.minecraft.resources.ResourceKey dimension, + final LevelStem levelStem, + final boolean isDebug, + final long biomeZoomSeed, + final List customSpawners, + final boolean tickTime, + final CallbackInfo ci ) { final SpongeServerLevelData spongeData = ((ServerLevelDataBridge) levelData).bridge$spongeData(); final ResourceKey worldKey = ((ServerWorld) this).key(); + + // Load per-dimension identity from SpongeRegistryData (SavedDataStorage) + final SpongeRegistryData registryData = this.shadow$getDataStorage().computeIfAbsent(SpongeRegistryData.TYPE); + if (registryData.key() == null) { + registryData.setKey(worldKey); + } + // Populate SpongeServerLevelData from registry data + spongeData.setUniqueId(registryData.uniqueId()); if (spongeData.key() == null) { - SpongeCommon.logger().warn("Level data ({}) has no key associated but is used by world {} ({}).", levelData.getClass().getSimpleName(), worldKey, this.getClass().getSimpleName()); spongeData.setKey(worldKey); } else if (!spongeData.key().equals(worldKey)) { SpongeCommon.logger().warn("Level data ({}) has key {} but is used by world {} ({}).", levelData.getClass().getSimpleName(), spongeData.key(), worldKey, this.getClass().getSimpleName()); @@ -178,16 +288,16 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel } this.impl$levelSave = storage; - this.impl$chunkProgressListener = listener; - this.impl$prevWeather = ((ServerWorld) this).weather(); + this.impl$prevWeather = SpongeWeather.of(this.shadow$getWeatherData()); ((LevelTicksBridge) this.blockTicks).bridge$level((ServerLevel) (Object) this); ((LevelTicksBridge) this.fluidTicks).bridge$level((ServerLevel) (Object) this); final Boolean createDragonFight = ((DimensionTypeBridge) (Object) this.shadow$dimensionType()).bridge$createDragonFight(); if (createDragonFight != null) { if (createDragonFight) { - final long seed = server.getWorldData().worldGenOptions().seed(); - this.dragonFight = new EndDragonFight((ServerLevel) (Object) this, seed, server.getWorldData().endDragonFightData()); + final long seed = spongeData.worldGenOptions().seed(); + this.dragonFight = this.shadow$getDataStorage().computeIfAbsent(EnderDragonFight.TYPE); + this.dragonFight.init((ServerLevel) (Object) this, seed, BlockPos.ZERO); } else { this.dragonFight = null; } @@ -201,11 +311,6 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel return this.impl$levelSave; } - @Override - public ChunkProgressListener bridge$getChunkProgressListener() { - return this.impl$chunkProgressListener; - } - @Override public boolean bridge$isLoaded() { if (((LevelBridge) this).bridge$isFake()) { @@ -229,7 +334,7 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel super.bridge$adjustDimensionLogic(dimensionType); ((DimensionDataStorageBridge) this.shadow$getDataStorage()).bridge$dimensionKey( - this.registryAccess().registryOrThrow(Registries.DIMENSION_TYPE).getKey(dimensionType)); + this.registryAccess().lookupOrThrow(Registries.DIMENSION_TYPE).getKey(dimensionType)); // TODO Minecraft 1.16.2 - Rebuild level stems, get generator from type, set generator // TODO ...or cache generator on type? @@ -240,10 +345,10 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel @Override public CustomBossEvents bridge$getBossBarManager() { if (this.impl$bossBarManager == null) { - if (Level.OVERWORLD.equals(this.shadow$dimension()) || this.bridge$isFake()) { + if (this.bridge$isFake()) { this.impl$bossBarManager = this.shadow$getServer().getCustomBossEvents(); } else { - this.impl$bossBarManager = new CustomBossEvents(); + this.impl$bossBarManager = this.shadow$getDataStorage().computeIfAbsent(CustomBossEvents.TYPE); } } @@ -252,38 +357,51 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel @Override public void bridge$triggerExplosion(Explosion explosion) { - // Sponge start // Set up the pre event + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) (Object) this); if (ShouldFire.EXPLOSION_EVENT_PRE) { - final ExplosionEvent.Pre - event = - SpongeEventFactory.createExplosionEventPre(PhaseTracker.getInstance().currentCause(), - explosion, (org.spongepowered.api.world.server.ServerWorld) this); + final var cause = phaseTracker.currentCause(); + final ExplosionEvent.Pre event = SpongeEventFactory.createExplosionEventPre(cause, explosion, (ServerWorld) this); if (SpongeCommon.post(event)) { return; } explosion = event.explosion(); } - final net.minecraft.world.level.Explosion mcExplosion = (net.minecraft.world.level.Explosion) explosion; + final ServerExplosion mcExplosion = (ServerExplosion) explosion; - try (final PhaseContext ignored = GeneralPhase.State.EXPLOSION.createPhaseContext(PhaseTracker.SERVER) - .explosion((net.minecraft.world.level.Explosion) explosion) - .source(explosion.sourceExplosive().isPresent() ? explosion.sourceExplosive() : this)) { + try (final PhaseContext ignored = GeneralPhase.State.EXPLOSION.createPhaseContext(phaseTracker) + .explosion(mcExplosion) + .source(explosion.sourceExplosive().isPresent() ? explosion.sourceExplosive() : this)) { ignored.buildAndSwitch(); - final boolean shouldBreakBlocks = explosion.shouldBreakBlocks(); - // Sponge End mcExplosion.explode(); - mcExplosion.finalizeExplosion(explosion.shouldPlaySmoke()); - if (!shouldBreakBlocks) { - mcExplosion.clearToBlow(); + // see ServerLevel#explode/Level#explode + ParticleOptions particle = mcExplosion.isSmall() ? ParticleTypes.EXPLOSION : ParticleTypes.EXPLOSION_EMITTER; + var sound = SoundEvents.GENERIC_EXPLODE; + for (ServerPlayer player : this.players) { + if (player.distanceToSqr(mcExplosion.center()) < 4096.0) { + Optional kb = Optional.ofNullable(mcExplosion.getHitPlayers().get(player)); + final var packet = new ClientboundExplodePacket(mcExplosion.center(), explosion.radius(), 1, kb, particle, sound, WeightedList.of()); + this.bridge$handleExplosionPacket(player.connection, explosion, packet); + player.connection.send(packet); + } } - - // Sponge Start - end processing } - // Sponge End + } + + @Override + public void bridge$handleExplosionPacket(final ServerGamePacketListenerImpl instance, Explosion apiExplosion, final ClientboundExplodePacket packet) { + // TODO control particle in API + ParticleOptions particleData = new BlockParticleOption(ParticleTypes.BLOCK, Blocks.AIR.defaultBlockState()); // "no" particle + particleData = packet.explosionParticle(); + // TODO control sound in API + var soundEvent = Holder.direct(new SoundEvent(Identifier.parse("sponge:none"), Optional.of(0f))); // "no" sound + soundEvent = packet.explosionSound(); + // TODO apiExplosion.shouldPlaySmoke() is not initialized correctly + var newPacket = new ClientboundExplodePacket(packet.center(), packet.radius(), packet.blockCount(), packet.playerKnockback(), particleData, soundEvent, packet.blockParticles()); + instance.send(newPacket); } @Override @@ -304,10 +422,10 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel } final SpongeBlockSnapshot.BuilderImpl builder = SpongeBlockSnapshot.BuilderImpl.pooled(); builder.world((ServerLevel) (Object) this).position(new Vector3i(x, y, z)); - final net.minecraft.world.level.chunk.LevelChunk chunk = this.shadow$getChunkAt(pos); - final net.minecraft.world.level.block.state.BlockState state = chunk.getBlockState(pos); + final LevelChunk chunk = this.shadow$getChunkAt(pos); + final BlockState state = chunk.getBlockState(pos); builder.blockState(state); - final net.minecraft.world.level.block.entity.BlockEntity blockEntity = chunk.getBlockEntity(pos, net.minecraft.world.level.chunk.LevelChunk.EntityCreationType.CHECK); + final BlockEntity blockEntity = chunk.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); if (blockEntity != null) { TrackingUtil.addTileEntityToBuilder(blockEntity, builder); } @@ -338,14 +456,6 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel return server.getWorldData(); } - @Redirect(method = "setDefaultSpawnPos", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/GameRules;getInt(Lnet/minecraft/world/level/GameRules$Key;)I")) - private int impl$respectKeepSpawnLoaded(final GameRules gameRules, final GameRules.Key key) { - if ((((ServerLevelDataBridge) this.shadow$getLevelData()).bridge$performsSpawnLogic())) { - return gameRules.getInt(key); - } - return 0; - } - @Inject(method = "save", at = @At("HEAD"), cancellable = true) public void impl$postSaveWorldEventPre(final CallbackInfo ci, final @Share("manualSave") LocalBooleanRef manualSave) { manualSave.set(this.impl$isManualSave); @@ -357,20 +467,13 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel } } - @WrapOperation(method = "save", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;saveLevelData()V")) - public void impl$wrapSaveLevelData(final ServerLevel self, final Operation original) { + @WrapOperation(method = "save", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;saveLevelData(Z)V")) + public void impl$wrapSaveLevelData(final ServerLevel self, final boolean flush, final Operation original) { final ServerLevelData levelData = (ServerLevelData) this.shadow$getLevelData(); final SerializationBehavior behavior = ((ServerLevelDataBridge) levelData).bridge$serializationBehavior().orElse(SerializationBehavior.AUTOMATIC); if (behavior != SerializationBehavior.NONE) { - original.call(self); - - // per-world WorldInfo/WorldBorder/BossBars - levelData.setWorldBorder(this.getWorldBorder().createSettings()); - if (levelData instanceof WorldData worldData) { - worldData.setCustomBossEvents(this.bridge$getBossBarManager().save(SpongeCommon.server().registryAccess())); - this.bridge$getLevelSave().saveDataTag(SpongeCommon.server().registryAccess(), worldData, this.shadow$dimension() == Level.OVERWORLD ? SpongeCommon.server().getPlayerList().getSingleplayerData() : null); - } + original.call(self, flush); } } @@ -387,12 +490,27 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel Sponge.eventManager().post(SpongeEventFactory.createSaveWorldEventPost(currentCause, (ServerWorld) this)); } + @WrapOperation(method = "advanceWeatherCycle", + at = { + @At(value = "INVOKE", target = "Lnet/minecraft/world/level/saveddata/WeatherData;isThundering()Z"), + @At(value = "INVOKE", target = "Lnet/minecraft/world/level/saveddata/WeatherData;isRaining()Z") + } + ) + private boolean impl$handleIsThunderingIfHasCeiling(final WeatherData instance, final Operation original) { + if (this.levelData instanceof PrimaryLevelDataBridge pldb && pldb.bridge$dimensionType() != null) { + // Dimensions with a ceiling (e.g. nether) should not have visible weather effects + return !pldb.bridge$dimensionType().hasCeiling() && original.call(instance); + } + return original.call(instance); + } + + // We need to stick with the local capture and coercion in 26.1 due to Forge stripping the LVT @Inject(method = "advanceWeatherCycle", - locals = LocalCapture.CAPTURE_FAILEXCEPTION, - at = @At(value = "FIELD", target = "Lnet/minecraft/server/level/ServerLevel;oRainLevel:F", shift = At.Shift.BEFORE, ordinal = 1)) - public void impl$onSetWeatherParameters(final CallbackInfo ci, final boolean $$0) { + locals = LocalCapture.CAPTURE_FAILEXCEPTION, + at = @At(value = "FIELD", target = "Lnet/minecraft/server/level/ServerLevel;oRainLevel:F", shift = At.Shift.BEFORE, ordinal = 1)) + public void impl$onSetWeatherParameters(final CallbackInfo ci, final @Coerce boolean wasRaining) { final boolean isRaining = this.shadow$isRaining(); - if (this.oRainLevel != this.rainLevel || this.oThunderLevel != this.thunderLevel || $$0 != isRaining) { + if (this.oRainLevel != this.rainLevel || this.oThunderLevel != this.thunderLevel || wasRaining != isRaining) { Weather newWeather = ((ServerWorld) this).properties().weather(); final Cause currentCause = PhaseTracker.getInstance().currentCause(); final Transaction weatherTransaction = new Transaction<>(this.impl$prevWeather, newWeather); @@ -404,34 +522,39 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel } // Set event results + // TODO - 26.1-snapshot-6 revamp weather with a timelines api. this.impl$prevWeather = newWeather; if (newWeather.type() == WeatherTypes.CLEAR.get()) { - this.serverLevelData.setThunderTime(0); - this.serverLevelData.setRainTime(0); - this.serverLevelData.setClearWeatherTime(SpongeTicks.toSaturatedIntOrInfinite(newWeather.remainingDuration())); - this.serverLevelData.setThundering(false); - this.serverLevelData.setRaining(false); + final var weatherData = this.shadow$getWeatherData(); + weatherData.setClearWeatherTime(SpongeTicks.toSaturatedIntOrInfinite(newWeather.remainingDuration())); + weatherData.setRainTime(0); + weatherData.setRaining(false); + weatherData.setThunderTime(0); + weatherData.setThundering(false); } else { final int newTime = SpongeTicks.toSaturatedIntOrInfinite(newWeather.remainingDuration()); - this.serverLevelData.setRaining(true); - this.serverLevelData.setClearWeatherTime(0); - this.serverLevelData.setRainTime(newTime); + final var weatherData = this.shadow$getWeatherData(); + weatherData.setRaining(true); + weatherData.setRainTime(newTime); + weatherData.setClearWeatherTime(0); if (newWeather.type() == WeatherTypes.THUNDER.get()) { - this.serverLevelData.setThunderTime(newTime); - this.serverLevelData.setThundering(true); + weatherData.setThundering(true); + weatherData.setThunderTime(newTime); } else { - this.serverLevelData.setThunderTime(0); - this.serverLevelData.setThundering(false); + weatherData.setThunderTime(0); + weatherData.setThundering(false); } } } } - @Redirect(method = "tickChunk", - at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;isRainingAt(Lnet/minecraft/core/BlockPos;)Z")) - private boolean impl$onBeforeThunder(final ServerLevel serverLevel, final BlockPos param0) { - final boolean rainingAt = serverLevel.isRainingAt(param0); + @WrapOperation(method = "tickThunder", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;isRainingAt(Lnet/minecraft/core/BlockPos;)Z")) + private boolean impl$onBeforeThunder( + final ServerLevel serverLevel, final BlockPos param0, final Operation wrapped + ) { + final boolean rainingAt = wrapped.call(serverLevel, param0); if (rainingAt) { final LightningEvent.Pre strike = SpongeEventFactory.createLightningEventPre(PhaseTracker.getInstance().currentCause()); if (Sponge.eventManager().post(strike)) { @@ -454,13 +577,13 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel } private void impl$setWorldOnBorder() { - ((WorldBorderBridge) this.shadow$getWorldBorder()).bridge$setAssociatedWorld(((ServerWorld) this).key()); + ((WorldBorderBridge) this.getWorldBorder()).bridge$setAssociatedWorld(((ServerWorld) this).key()); } @Inject(method = "globalLevelEvent", at = @At("HEAD"), cancellable = true) private void impl$throwBroadcastGlobalEvent(int effectID, BlockPos pos, int pitch, CallbackInfo ci) { if (!this.bridge$isFake() && ShouldFire.PLAY_SOUND_EVENT_BROADCAST) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.SERVER.pushCauseFrame()) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getWorldInstance((ServerLevel) (Object) this).pushCauseFrame()) { final PlaySoundEvent.Broadcast event = SpongeCommonEventFactory.callPlaySoundBroadcastEvent(frame, this, pos, effectID); if (event != null && event.isCancelled()) { ci.cancel(); @@ -470,18 +593,18 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel } @Inject(method = "levelEvent", at = @At("HEAD"), cancellable = true) - private void impl$throwBroadcastEvent(final Player player, final int eventID, final BlockPos pos, final int dataID, CallbackInfo ci) { - if(eventID == Constants.WorldEvents.PLAY_RECORD_EVENT && ShouldFire.PLAY_SOUND_EVENT_FROM_JUKEBOX) { + private void impl$throwBroadcastEvent(final Entity player, final int eventID, final BlockPos pos, final int dataID, CallbackInfo ci) { + if (eventID == Constants.WorldEvents.PLAY_RECORD_EVENT && ShouldFire.PLAY_SOUND_EVENT_FROM_JUKEBOX) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { final BlockEntity tileEntity = this.shadow$getBlockEntity(pos); - if(tileEntity instanceof JukeboxBlockEntity) { + if (tileEntity instanceof JukeboxBlockEntity) { final JukeboxBlockEntity jukebox = (JukeboxBlockEntity) tileEntity; final ItemStack record = jukebox.getItem(0); frame.pushCause(jukebox); frame.addContext(EventContextKeys.USED_ITEM, ItemStackUtil.snapshotOf(record)); if (!record.isEmpty()) { final Optional recordProperty = ((org.spongepowered.api.item.inventory.ItemStack) (Object) record).get(Keys.MUSIC_DISC); - if(!recordProperty.isPresent()) { + if (!recordProperty.isPresent()) { //Safeguard for https://github.com/SpongePowered/SpongeCommon/issues/2337 return; } @@ -503,18 +626,20 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel } } - @Redirect(method = "lambda$onBlockStateChange$13", + @WrapOperation(method = "lambda$updatePOIOnBlockStateChange$3", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/entity/ai/village/poi/PoiManager;add(Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/Holder;)V" + target = "Lnet/minecraft/world/entity/ai/village/poi/PoiManager;add(Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/Holder;)Lnet/minecraft/world/entity/ai/village/poi/PoiRecord;" ) ) - private void impl$avoidAddingPoiUpdatesOnUnloadedWorld(final PoiManager manager, final BlockPos pos, final Holder type) { + private PoiRecord impl$avoidAddingPoiUpdatesOnUnloadedWorld( + final PoiManager instance, final BlockPos pos, final Holder type, final Operation original + ) { // Unloaded worlds should not notify PoiManager of changes if (!SpongeCommon.server().levelKeys().contains(this.shadow$dimension())) { - return; + return null; } - manager.add(pos, type); + return original.call(instance, pos, type); } @Inject( @@ -542,20 +667,79 @@ public abstract class ServerLevelMixin extends LevelMixin implements ServerLevel @Override public String toString() { - final Optional worldTypeKey = Optional.ofNullable(this.server.registryAccess().registryOrThrow(Registries.DIMENSION_TYPE).getKey(this.shadow$dimensionType())).map(ResourceKey.class::cast); + final Optional worldTypeKey = Optional.ofNullable(this.server.registryAccess().lookupOrThrow(Registries.DIMENSION_TYPE).getKey(this.shadow$dimensionType())).map(ResourceKey.class::cast); return new StringJoiner(",", ServerLevel.class.getSimpleName() + "[", "]") - .add("key=" + this.shadow$dimension()) - .add("worldType=" + worldTypeKey.map(ResourceKey::toString).orElse("inline")) - .toString(); + .add("key=" + this.shadow$dimension()) + .add("worldType=" + worldTypeKey.map(ResourceKey::toString).orElse("inline")) + .toString(); } - @Redirect(method = { - "advanceWeatherCycle", - "globalLevelEvent", - "setDefaultSpawnPos" - }, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcastAll(Lnet/minecraft/network/protocol/Packet;)V")) - private void impl$broadcastAllCurrentDimensionOnly(final PlayerList instance, final Packet $$0) { - //Weather, game rules and spawns are per world in Sponge. - instance.broadcastAll($$0, this.shadow$dimension()); + @Override + @SuppressWarnings("unchecked") + public E bridge$createEntity(final EntityType type, final Vector3d position, final boolean naturally) throws IllegalArgumentException, IllegalStateException { + if (type == net.minecraft.world.entity.EntityTypes.PLAYER) { + // Unable to construct these + throw new IllegalArgumentException("A Player cannot be created by the API!"); + } + + Entity entity = null; + final double x = position.x(); + final double y = position.y(); + final double z = position.z(); + final Level thisWorld = (Level) (Object) this; + // Not all entities have a single World parameter as their constructor + if (type == net.minecraft.world.entity.EntityTypes.LIGHTNING_BOLT) { + entity = net.minecraft.world.entity.EntityTypes.LIGHTNING_BOLT.create(thisWorld, EntitySpawnReason.EVENT); + entity.snapTo(x, y, z); + ((LightningBolt) entity).setVisualOnly(false); + } + // TODO - archetypes should solve the problem of calling the correct constructor + if (type == net.minecraft.world.entity.EntityTypes.ENDER_PEARL) { + final ArmorStand tempEntity = new ArmorStand(thisWorld, x, y, z); + tempEntity.setPos(tempEntity.getX(), tempEntity.getY() - tempEntity.getEyeHeight(), tempEntity.getZ()); + entity = new ThrownEnderpearl(thisWorld, tempEntity, Items.ENDER_PEARL.getDefaultInstance()); + ((EnderPearl) entity).offer(Keys.SHOOTER, UnknownProjectileSource.UNKNOWN); + } + // Some entities need to have non-null fields (and the easiest way to + // set them is to use the more specialised constructor). + if (type == net.minecraft.world.entity.EntityTypes.FALLING_BLOCK) { + entity = FallingBlockEntityAccessor.invoker$new(thisWorld, x, y, z, Blocks.SAND.defaultBlockState()); + } + if (type == net.minecraft.world.entity.EntityTypes.ITEM) { + entity = new ItemEntity(thisWorld, x, y, z, new ItemStack(Blocks.STONE)); + } + + if (entity == null) { + final ResourceKey key = (ResourceKey) (Object) SpongeCommon.vanillaRegistry(Registries.ENTITY_TYPE).getKey((net.minecraft.world.entity.EntityType) type); + try { + entity = ((net.minecraft.world.entity.EntityType) type).create(thisWorld, EntitySpawnReason.EVENT); + entity.snapTo(x, y, z); + } catch (final Exception e) { + throw new RuntimeException("There was an issue attempting to construct " + key, e); + } + } + + // TODO - replace this with an actual check + + if (entity instanceof HangingEntity) { + if (!((HangingEntity) entity).survives()) { + throw new IllegalArgumentException("Hanging entity does not survive at the given position: " + position); + } + } + + if (naturally && entity instanceof Mob) { + // Adding the default equipment + final DifficultyInstance difficulty = this.shadow$getCurrentDifficultyAt(BlockPos.containing(x, y, z)); + ((MobAccessor) entity).invoker$populateDefaultEquipmentSlots(this.random, difficulty); + } + + if (entity instanceof Painting) { + // This is default when art is null when reading from NBT, could + // choose a random art instead? + // TODO ? ((Painting) entity).motive = Motive.KEBAB; + } + + return (E) entity; } + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerGameModeMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerGameModeMixin.java index 204fb40efa2..b383b45e230 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerGameModeMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerGameModeMixin.java @@ -111,7 +111,7 @@ public abstract class ServerPlayerGameModeMixin implements ServerPlayerGameModeB @WrapMethod(method = "destroyBlock") private boolean impl$onDestroyBlock(final BlockPos $$0, final Operation original) { - final PhaseTracker tracker = PhaseTracker.SERVER; + final PhaseTracker tracker = PhaseTracker.getWorldInstance(this.level); try (final CauseStackManager.StackFrame frame = tracker.pushCauseFrame()) { final BlockSnapshot snapshot = ((org.spongepowered.api.world.server.ServerWorld) this.level) .createSnapshot(VecHelper.toVector3i($$0)); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin.java index ee3eff0b7ab..ef3e7d78379 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin.java @@ -27,33 +27,33 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Cancellable; +import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalRef; import com.mojang.datafixers.util.Either; import io.netty.channel.Channel; import net.kyori.adventure.text.Component; -import net.minecraft.advancements.CriteriaTriggers; +import net.minecraft.advancements.triggers.CriteriaTriggers; import net.minecraft.core.BlockPos; +import net.minecraft.core.GlobalPos; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.ChatType; import net.minecraft.network.chat.OutgoingChatMessage; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; -import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket; -import net.minecraft.network.protocol.game.ClientboundGameEventPacket; -import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket; -import net.minecraft.network.protocol.game.ClientboundRespawnPacket; import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.ServerScoreboard; import net.minecraft.server.level.ClientInformation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayerGameMode; -import net.minecraft.server.level.TicketType; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.server.players.PlayerList; import net.minecraft.stats.Stats; @@ -64,20 +64,22 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.animal.AbstractFish; +import net.minecraft.world.entity.Relative; +import net.minecraft.world.entity.animal.fish.AbstractFish; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.GameRules; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.level.border.WorldBorder; -import net.minecraft.world.level.portal.DimensionTransition; +import net.minecraft.world.level.gamerules.GameRules; +import net.minecraft.world.level.portal.TeleportTransition; import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.phys.Vec3; import net.minecraft.world.scores.PlayerTeam; import org.checkerframework.checker.nullness.qual.Nullable; +import org.objectweb.asm.Opcodes; import org.spongepowered.api.Sponge; import org.spongepowered.api.adventure.Audiences; import org.spongepowered.api.block.BlockSnapshot; @@ -96,11 +98,8 @@ import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.cause.entity.MovementTypes; import org.spongepowered.api.event.data.ChangeDataHolderEvent; -import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; import org.spongepowered.api.event.entity.DestructEntityEvent; import org.spongepowered.api.event.entity.InteractEntityEvent; -import org.spongepowered.api.event.entity.MoveEntityEvent; -import org.spongepowered.api.event.entity.RotateEntityEvent; import org.spongepowered.api.event.entity.living.player.KickPlayerEvent; import org.spongepowered.api.event.entity.living.player.PlayerChangeClientSettingsEvent; import org.spongepowered.api.event.entity.living.player.RespawnPlayerEvent; @@ -127,8 +126,9 @@ import org.spongepowered.common.accessor.network.ConnectionAccessor; import org.spongepowered.common.accessor.server.level.ChunkMapAccessor; import org.spongepowered.common.accessor.server.level.ChunkMap_TrackedEntityAccessor; +import org.spongepowered.common.accessor.server.level.ServerPlayerAccessor; import org.spongepowered.common.accessor.server.network.ServerCommonPacketListenerImplAccessor; -import org.spongepowered.common.accessor.world.level.portal.DimensionTransitionAccessor; +import org.spongepowered.common.accessor.world.level.portal.TeleportTransitionAccessor; import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.data.DataCompoundHolder; import org.spongepowered.common.bridge.data.SpongeDataHolderBridge; @@ -145,18 +145,13 @@ import org.spongepowered.common.event.ShouldFire; import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.event.tracking.phase.entity.EntityPhase; -import org.spongepowered.common.event.tracking.phase.entity.TeleportContext; -import org.spongepowered.common.hooks.PlatformHooks; import org.spongepowered.common.mixin.core.world.entity.player.PlayerMixin; import org.spongepowered.common.network.packet.SpongePacketHandler; import org.spongepowered.common.util.LocaleCache; -import org.spongepowered.common.util.VecHelper; import org.spongepowered.common.world.border.PlayerOwnBorderListener; import org.spongepowered.math.vector.Vector3d; import java.util.HashMap; -import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -170,23 +165,15 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr // @formatter:off @Shadow public ServerGamePacketListenerImpl connection; @Shadow @Final public ServerPlayerGameMode gameMode; - @Shadow @Final public MinecraftServer server; + @Shadow @Final private MinecraftServer server; @Shadow private int lastRecordedExperience; - @Shadow private boolean isChangingDimension; - @Shadow private net.minecraft.world.phys.Vec3 enteredNetherPosition; - @Shadow private int lastSentExp; - @Shadow private float lastSentHealth; - @Shadow private int lastSentFood; - @Shadow private ResourceKey respawnDimension; - @Shadow @Nullable private BlockPos respawnPosition; - @Shadow private boolean respawnForced; - @Shadow private float respawnAngle; - - @Shadow public abstract ServerLevel shadow$serverLevel(); - @Shadow protected abstract void shadow$triggerDimensionChangeTriggers(ServerLevel serverworld); + @Shadow @javax.annotation.Nullable private net.minecraft.server.level.ServerPlayer.RespawnConfig respawnConfig; + + @Shadow public abstract ServerLevel shadow$level(); @Shadow public abstract void shadow$doCloseContainer(); @Shadow public abstract boolean shadow$setGameMode(GameType param0); - @Shadow public abstract void shadow$setRespawnPosition(ResourceKey dimension, @Nullable BlockPos pos, float angle, boolean forced, boolean sendMessage); + @Shadow public abstract void shadow$setCamera(@org.jetbrains.annotations.Nullable final Entity $$0); + @Shadow public abstract void shadow$setRespawnPosition(@javax.annotation.Nullable net.minecraft.server.level.ServerPlayer.RespawnConfig config, boolean sendMessage); // @formatter:on private net.minecraft.network.chat.@Nullable Component impl$connectionMessage; @@ -233,29 +220,21 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr return Tristate.FALSE; } - @Override - protected final boolean impl$setLocation(final boolean isChangeOfWorld, final ServerLevel level, final Vector3d pos) { - if (this.shadow$isRemoved()) { - return false; - } - final var thisPlayer = ((net.minecraft.server.level.ServerPlayer) (Object) this); - - final ChunkPos chunkPos = new ChunkPos(VecHelper.toBlockPos(pos)); - level.getChunkSource().addRegionTicket(TicketType.POST_TELEPORT, chunkPos, 1, thisPlayer.getId()); - - thisPlayer.stopRiding(); - if (thisPlayer.isSleeping()) { - thisPlayer.stopSleepInBed(true, true); - } - if (!isChangeOfWorld) { - this.connection.teleport(pos.x(), pos.y(), pos.z(), this.shadow$getYRot(), this.shadow$getXRot(), new HashSet<>()); - this.connection.resetPosition(); - } else { - this.bridge$changeDimension(new DimensionTransition(level, VecHelper.toVanillaVector3d(pos), thisPlayer.getKnownMovement(), - this.shadow$getYRot(), this.shadow$getXRot(), DimensionTransition.DO_NOTHING)); + @WrapOperation( + method = "handleShoulderEntities", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/level/ServerPlayer;playShoulderEntityAmbientSound(Lnet/minecraft/nbt/CompoundTag;)V" + ) + ) + private void impl$ignoreShoulderSoundsWhileVanished( + final net.minecraft.server.level.ServerPlayer thisPlayer, final CompoundTag tag, final Operation original + ) { + if (!this.bridge$vanishState().createsSounds()) { + return; } - return true; + original.call(thisPlayer, tag); } @Override @@ -304,7 +283,7 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr @Override public void bridge$initScoreboard() { - ((ServerScoreboardBridge) this.shadow$getScoreboard()).bridge$addPlayer((net.minecraft.server.level.ServerPlayer) (Object) this, true); + ((ServerScoreboardBridge) this.shadow$level().getScoreboard()).bridge$addPlayer((net.minecraft.server.level.ServerPlayer) (Object) this, true); } @Override @@ -334,15 +313,15 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr @Override public boolean bridge$keepInventory() { - return Objects.requireNonNullElseGet(this.impl$keepInventory, () -> this.shadow$level().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY)); + return Objects.requireNonNullElseGet(this.impl$keepInventory, () -> this.shadow$level().getGameRules().get(GameRules.KEEP_INVENTORY)); } @Override protected void impl$dropInventoryWrapForPlayerOverride( - final LivingEntity instance, final Operation original + final LivingEntity instance, final ServerLevel level, final Operation original ) { if (this.impl$keepInventory == null || !this.impl$keepInventory) { - original.call(instance); + original.call(instance, level); } } @@ -414,210 +393,46 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr this.connection.send(packet); } - /* - @Inject(method = "markPlayerActive()V", at = @At("HEAD")) - private void impl$onPlayerActive(final CallbackInfo ci) { - ((ServerPlayNetHandlerBridge) this.connection).bridge$resendLatestResourcePackRequest(); - } -*/ - - /** - * @author faithcaio - 2024-05-30 - MC 1.21 - * @reason Redirect all teleports through {@link #bridge$changeDimension} to fire our move/rotate/teleport events - */ - @Overwrite - public void teleportTo(final ServerLevel world, final double x, final double y, final double z, final float yaw, final float pitch) { - final boolean hasMovementContext = PhaseTracker.getInstance().currentContext().containsKey(EventContextKeys.MOVEMENT_TYPE); - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - if (!hasMovementContext) { - frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.PLUGIN); - } - - final var thisPlayer = (net.minecraft.server.level.ServerPlayer) (Object) this; - this.bridge$changeDimension(new DimensionTransition(world, new Vec3(x, y, z), Vec3.ZERO, yaw, pitch, - e -> world.getChunkSource().addRegionTicket(TicketType.POST_TELEPORT, e.chunkPosition(), 1, thisPlayer.getId()))); + @WrapOperation(method = "setCamera", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;teleportTo(Lnet/minecraft/server/level/ServerLevel;DDDLjava/util/Set;FFZ)Z")) + private boolean impl$onSpectatorTeleport( + final net.minecraft.server.level.ServerPlayer instance, final ServerLevel level, final double x, final double y, final double z, + final Set relatives, final float xRot, final float yRot, final boolean setCamera, final Operation original + ) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getWorldInstance(level).pushCauseFrame()) { + frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.SPECTATOR); + return original.call(instance, level, x, y, z, relatives, xRot, yRot, setCamera); } } - /** - * This is effectively an overwrite of changeDimension and teleportTo. - * Handles {@link MoveEntityEvent} -> {@link RotateEntityEvent} for in dimension teleport - * Handles {@link ChangeEntityWorldEvent.Reposition} -> rotate -> {@link ChangeEntityWorldEvent.Post} - * {@link ChangeEntityWorldEvent.Pre} is handled at call sites. TODO only for known portals? - * For known portals {@link ChangeEntityWorldEvent.Reposition} is handled before at {@link Entity#handlePortal()} - * - * @return The {@link Entity} that is either this one, or replaces this one - */ - @Override - public @Nullable Entity bridge$changeDimension(final DimensionTransition originalTransition) { - if (this.shadow$isRemoved()) { - return null; - } - - final var thisPlayer = (net.minecraft.server.level.ServerPlayer) (Object) this; - - if (originalTransition.missingRespawnBlock()) { // Player only code - this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.NO_RESPAWN_BLOCK_AVAILABLE, 0.0F)); + @WrapMethod(method = "teleport(Lnet/minecraft/world/level/portal/TeleportTransition;)Lnet/minecraft/server/level/ServerPlayer;") + private net.minecraft.server.level.ServerPlayer impl$wrapTeleport(final TeleportTransition transition, Operation original) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(this.shadow$level()); + try (final CauseStackManager.StackFrame frame = phaseTracker.pushCauseFrame()) { + frame.pushCause(this); + return original.call(transition); } + } - final var originalNewLevel = originalTransition.newLevel(); - final var oldLevel = this.shadow$serverLevel(); - - // SpongeStart - final var transition = this.impl$fireDimensionTransitionEvents(originalTransition, thisPlayer); + @ModifyVariable(method = "teleport(Lnet/minecraft/world/level/portal/TeleportTransition;)Lnet/minecraft/server/level/ServerPlayer;", argsOnly = true, + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/portal/TeleportTransition;missingRespawnBlock()Z")) + private TeleportTransition impl$beforeTeleport(TeleportTransition transition, @Share("original-new-level") final LocalRef originalNewLevelRef, + @Cancellable final CallbackInfoReturnable cir) { + final ServerLevel originalNewLevel = transition.newLevel(); + originalNewLevelRef.set(originalNewLevel); + transition = originalNewLevel == this.shadow$level() ? this.impl$fireTeleportSameDimension(transition) : this.impl$fireTeleportCrossDimensionBefore(transition); if (transition == null) { - return null; - } - final var newLevel = transition.newLevel(); - // Sponge End - - if (newLevel.dimension() == oldLevel.dimension()) { // actually no dimension change - this.connection.teleport(transition.pos().x, transition.pos().y, transition.pos().z, transition.yRot(), transition.xRot()); - this.connection.resetPosition(); - transition.postDimensionTransition().onTransition(thisPlayer); - // TODO setYHeadRot after would rotate event result - return thisPlayer; - } - this.isChangingDimension = true; - LevelData lvlData = newLevel.getLevelData(); - this.connection.send(new ClientboundRespawnPacket(thisPlayer.createCommonSpawnInfo(newLevel), ClientboundRespawnPacket.KEEP_ALL_DATA)); - this.connection.send(new ClientboundChangeDifficultyPacket(lvlData.getDifficulty(), lvlData.isDifficultyLocked())); - PlayerList playerList = this.server.getPlayerList(); - playerList.sendPlayerPermissionLevel(thisPlayer); - oldLevel.removePlayerImmediately(thisPlayer, Entity.RemovalReason.CHANGED_DIMENSION); - this.shadow$unsetRemoved(); - - oldLevel.getProfiler().push("moving"); - if (oldLevel.dimension() == Level.OVERWORLD && newLevel.dimension() == Level.NETHER) { - this.enteredNetherPosition = thisPlayer.position(); + cir.setReturnValue(null); } - oldLevel.getProfiler().pop(); - - oldLevel.getProfiler().push("placing"); - thisPlayer.setServerLevel(newLevel); - this.connection.teleport(transition.pos().x, transition.pos().y, transition.pos().z, transition.yRot(), transition.xRot()); - this.connection.resetPosition(); - newLevel.addDuringTeleport(thisPlayer); - oldLevel.getProfiler().pop(); - - this.shadow$triggerDimensionChangeTriggers(oldLevel); // TODO old sponge EntityUtil#performPostChangePlayerWorldLogic this was only done when using a portal - this.connection.send(new ClientboundPlayerAbilitiesPacket(thisPlayer.getAbilities())); - playerList.sendLevelInfo(thisPlayer, newLevel); - playerList.sendAllPlayerInfo(thisPlayer); - playerList.sendActivePlayerEffects(thisPlayer); - transition.postDimensionTransition().onTransition(thisPlayer); - // TODO old sponge EntityUtil#performPostChangePlayerWorldLogic called bridge$getBossBarManager().onPlayerDisconnect(player); on both worlds - // TODO old sponge EntityUtil#performPostChangePlayerWorldLogic closed player.closeContainer(); when open - this.lastSentExp = -1; - this.lastSentHealth = -1.0F; - this.lastSentFood = -1; - // Sponge Start TODO cause/context like in impl$fireDimensionTransitionEvents - Sponge.eventManager().post( - SpongeEventFactory.createChangeEntityWorldEventPost( - PhaseTracker.getInstance().currentCause(), - (org.spongepowered.api.entity.Entity) this, - (ServerWorld) oldLevel, - (ServerWorld) newLevel, - (ServerWorld) originalNewLevel - ) - ); - // Sponge End - return thisPlayer; + return transition; } - @Unique - @Nullable - private DimensionTransition impl$fireDimensionTransitionEvents( - final DimensionTransition originalTransition, - final net.minecraft.server.level.ServerPlayer thisPlayer + @Inject(method = "teleport(Lnet/minecraft/world/level/portal/TeleportTransition;)Lnet/minecraft/server/level/ServerPlayer;", + at = @At(value = "FIELD", target = "Lnet/minecraft/server/level/ServerPlayer;lastSentFood:I", opcode = Opcodes.PUTFIELD, shift = At.Shift.AFTER)) + private void impl$afterTeleportCrossDimension( + final TeleportTransition transition, final CallbackInfoReturnable cir, + @Local(ordinal = 1) final ServerLevel oldLevel, @Share("original-new-level") LocalRef originalNewLevel ) { - var transition = originalTransition; - var isDimensionChange = transition.newLevel() != thisPlayer.serverLevel(); - - if (!this.impl$moveEventsFired) { - final var contextToSwitchTo = EntityPhase.State.PORTAL_DIMENSION_CHANGE.createPhaseContext(PhaseTracker.getInstance()).worldChange() - .player(); - final boolean hasMovementContext = PhaseTracker.SERVER.currentContext().containsKey(EventContextKeys.MOVEMENT_TYPE); - try (final TeleportContext context = contextToSwitchTo.buildAndSwitch(); - final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - frame.pushCause(thisPlayer); - if (!hasMovementContext) { - // TODO we should be able to detect normal plugin code though - // add an unknown movement type? - frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.PLUGIN); - } - - final var originalDest = VecHelper.toVector3d(transition.pos()); - final @Nullable Vector3d newDest; - - if (isDimensionChange) { - if (transition.newLevel() != thisPlayer.level()) { - final ChangeEntityWorldEvent.Pre preEvent = PlatformHooks.INSTANCE.getEventHooks().callChangeEntityWorldEventPre(thisPlayer, transition.newLevel()); - if (SpongeCommon.post(preEvent)) { - return null; - } - if (preEvent.destinationWorld() != preEvent.originalDestinationWorld()) { - transition = new DimensionTransition((ServerLevel) preEvent.destinationWorld(), - transition.pos(), - transition.speed(), - transition.yRot(), transition.xRot(), - transition.missingRespawnBlock(), - transition.postDimensionTransition()); - } - } - - final var reposition = this.bridge$fireRepositionEvent((ServerWorld) thisPlayer.serverLevel(), (ServerWorld) transition.newLevel(), originalDest); - if (reposition.isCancelled()) { - return null; // we did not move yet so just return - } - newDest = reposition.destinationPosition(); - } else { - if (ShouldFire.MOVE_ENTITY_EVENT) { // TODO move into impl$fireMoveEvent? - newDest = this.impl$fireMoveEvent(PhaseTracker.SERVER, originalDest); - if (newDest == null) { - return null; - } - } else { - newDest = originalDest; - } - } - if (newDest != originalDest) { - // if changed override the DimensionTransition - transition = new DimensionTransition(transition.newLevel(), - VecHelper.toVanillaVector3d(newDest), - transition.speed(), - transition.yRot(), transition.xRot(), - transition.missingRespawnBlock(), - transition.postDimensionTransition()); - } - - final Vector3d toRot = new Vector3d(transition.xRot(), transition.yRot(), 0); - final Vector3d fromRot = new Vector3d(thisPlayer.getXRot(), thisPlayer.getYRot(), 0); - // TODO this skips with fuzzy rotation change check. Do we want this? - var newToRot = SpongeCommonEventFactory.callRotateEvent((org.spongepowered.api.entity.Entity) thisPlayer, fromRot, toRot); - if (newToRot == null) { - newToRot = fromRot; // Cancelled Rotate - Reset to original rotation - } - if (toRot != newToRot) { - transition = new DimensionTransition(transition.newLevel(), - transition.pos(), - transition.speed(), - (float) newToRot.y(), (float) newToRot.x(), - transition.missingRespawnBlock(), - transition.postDimensionTransition()); - } - - } - - } - - thisPlayer.setCamera(thisPlayer); - thisPlayer.stopRiding(); - if (thisPlayer.isSleeping()) { - thisPlayer.stopSleepInBed(true, true); - } - - return transition; + this.impl$fireTeleportCrossDimensionAfter(oldLevel, transition.newLevel(), originalNewLevel.get()); } @Redirect( @@ -653,19 +468,19 @@ public void teleportTo(final ServerLevel world, final double x, final double y, @ModifyExpressionValue( method = "die", at = @At( - value = "INVOKE", target = "Lnet/minecraft/world/level/GameRules;getBoolean(Lnet/minecraft/world/level/GameRules$Key;)Z" + value = "INVOKE", target = "Lnet/minecraft/world/level/gamerules/GameRules;get(Lnet/minecraft/world/level/gamerules/GameRule;)Ljava/lang/Object;" ), slice = @Slice( from = @At("HEAD"), to = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/CombatTracker;getDeathMessage()Lnet/minecraft/network/chat/Component;") ) ) - private boolean impl$onlySendMessageIfEventCallsForIt( - boolean gameRules, + private Object impl$onlySendMessageIfEventCallsForIt( + Object gameRules, @Share("sponge-event") LocalRef event ) { final var spongeEvent = event.get(); - return gameRules && (spongeEvent == null || !spongeEvent.isMessageCancelled()); + return (Boolean) gameRules && (spongeEvent == null || !spongeEvent.isMessageCancelled()); } @ModifyExpressionValue( @@ -729,10 +544,10 @@ public void teleportTo(final ServerLevel world, final double x, final double y, @ModifyExpressionValue( method = "restoreFrom", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/GameRules;getBoolean(Lnet/minecraft/world/level/GameRules$Key;)Z") + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/gamerules/GameRules;get(Lnet/minecraft/world/level/gamerules/GameRule;)Ljava/lang/Object;") ) - private boolean tracker$useKeepFromBridge( - boolean original, + private Object tracker$useKeepFromBridge( + Object original, net.minecraft.server.level.ServerPlayer corpse, boolean wonGame ) { @@ -828,14 +643,24 @@ public void sendMessage(final OutgoingChatMessage $$0, final boolean $$1, final } } - @Override - public net.minecraft.world.scores.Scoreboard shadow$getScoreboard() { - return (net.minecraft.world.scores.Scoreboard) this.impl$scoreboard; + @WrapOperation(method = { + "updateScoreForCriteria", + "die", + "awardKillScore", + "handleTeamKill", + "awardStat", + "resetStat", + }, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;getScoreboard()Lnet/minecraft/server/ServerScoreboard;")) + private ServerScoreboard impl$usePerPlayerScoreboard( + final ServerLevel instance, final Operation original + ) { + return (ServerScoreboard) this.impl$scoreboard; } @Override protected void impl$onRightClickEntity( - final Entity entityToInteractOn, final InteractionHand hand, final CallbackInfoReturnable cir + final Entity entityToInteractOn, final InteractionHand hand, + final Vec3 location, final CallbackInfoReturnable cir ) { final ItemStack itemInHand = this.shadow$getItemInHand(hand); final InteractEntityEvent.Secondary event = SpongeCommonEventFactory.callInteractEntityEventSecondary((net.minecraft.server.level.ServerPlayer) (Object) this, @@ -862,29 +687,21 @@ public void sendMessage(final OutgoingChatMessage $$0, final boolean $$1, final @Inject(method = "startSleepInBed", at = @At(value = "RETURN"), cancellable = true) private void impl$onReturnSleep(final BlockPos param0, final CallbackInfoReturnable> cir) { final Either returnValue = cir.getReturnValue(); - if (returnValue.left().isPresent()) { - switch (returnValue.left().get()) { - - case NOT_POSSIBLE_HERE: - case TOO_FAR_AWAY: - case NOT_POSSIBLE_NOW: - case OBSTRUCTED: - case NOT_SAFE: - final Cause currentCause = PhaseTracker.getInstance().currentCause(); - final BlockSnapshot snapshot = ((ServerWorld) this.shadow$level()).createSnapshot(param0.getX(), param0.getY(), param0.getZ()); - if (Sponge.eventManager().post(SpongeEventFactory.createSleepingEventFailed(currentCause, snapshot, (Living) this))) { - final Either var5 = super.shadow$startSleepInBed(param0).ifRight((param0x) -> { - this.shadow$awardStat(Stats.SLEEP_IN_BED); - CriteriaTriggers.SLEPT_IN_BED.trigger((net.minecraft.server.level.ServerPlayer) (Object) this); - }); - ((ServerLevel) this.shadow$level()).updateSleepingPlayerList(); - cir.setReturnValue(var5); - } - break; - case OTHER_PROBLEM: // ignore - break; + returnValue.ifLeft(problem -> { + if (problem.message() == null) { + return; } - } + final Cause currentCause = PhaseTracker.getInstance().currentCause(); + final BlockSnapshot snapshot = ((ServerWorld) this.shadow$level()).createSnapshot(param0.getX(), param0.getY(), param0.getZ()); + if (Sponge.eventManager().post(SpongeEventFactory.createSleepingEventFailed(currentCause, snapshot, (Living) this))) { + final Either var5 = super.shadow$startSleepInBed(param0).ifRight((param0x) -> { + this.shadow$awardStat(Stats.SLEEP_IN_BED); + CriteriaTriggers.SLEPT_IN_BED.trigger((net.minecraft.server.level.ServerPlayer) (Object) this); + }); + ((ServerLevel) this.shadow$level()).updateSleepingPlayerList(); + cir.setReturnValue(var5); + } + }); } @Override @@ -951,60 +768,88 @@ public void sendMessage(final OutgoingChatMessage $$0, final boolean $$1, final */ @Overwrite private boolean isPvpAllowed() { - return ((ServerWorld) this.shadow$serverLevel()).properties().pvp(); - } - - @Overwrite - public Entity changeDimension(final DimensionTransition transition) { - return this.bridge$changeDimension(transition); + return ((ServerWorld) this.shadow$level()).properties().pvp(); } - @Redirect(method = "findRespawnPositionAndUseSpawnBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;getRespawnDimension()Lnet/minecraft/resources/ResourceKey;")) - private ResourceKey impl$callRespawnPlayerSelectWorld(final net.minecraft.server.level.ServerPlayer player) { + @WrapOperation(method = "findRespawnPositionAndUseSpawnBlock", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/server/level/ServerPlayer;getRespawnConfig()Lnet/minecraft/server/level/ServerPlayer$RespawnConfig;" + )) + private net.minecraft.server.level.ServerPlayer.RespawnConfig impl$callRespawnPlayerSelectWorld( + final net.minecraft.server.level.ServerPlayer player, + final Operation original, + @Share("sponge:overridden-respawn") final LocalRef> dimension + ) { // Prefer a forced Keys.RESPAWN_LOCATIONS entry for the world the player died in, even if vanilla's single - // slot points elsewhere. Mirror it into vanilla's fields up front so findRespawnPositionAndUseSpawnBlock - // reads the correct BlockPos when locating the bed/anchor. - final org.spongepowered.api.ResourceKey dyingWorldKey = (org.spongepowered.api.ResourceKey) (Object) player.serverLevel().dimension().location(); + // slot points elsewhere. Mirror it into vanilla's respawnConfig up front so findRespawnPositionAndUseSpawnBlock + // reads the correct position when locating the bed/anchor. + final org.spongepowered.api.ResourceKey dyingWorldKey = (org.spongepowered.api.ResourceKey) (Object) player.level().dimension().identifier(); final RespawnLocation forcedInDyingWorld = this.impl$bedLocations.get(dyingWorldKey); if (forcedInDyingWorld != null && forcedInDyingWorld.isForced()) { this.impl$syncingRespawn = true; try { - final ResourceKey dim = ResourceKey.create(Registries.DIMENSION, - (ResourceLocation) (Object) forcedInDyingWorld.worldKey()); - final Vector3d pos = forcedInDyingWorld.position(); - this.shadow$setRespawnPosition(dim, BlockPos.containing(pos.x(), pos.y(), pos.z()), - this.shadow$getYRot(), true, false); + this.shadow$setRespawnPosition(this.impl$toRespawnConfig(forcedInDyingWorld), false); } finally { this.impl$syncingRespawn = false; } } - var playerRespawnDestination = this.server.getLevel(player.getRespawnDimension()); + final var config = original.call(player); + final var defaulted = config == null ? Level.OVERWORLD : config.respawnData().dimension(); + + var playerRespawnDestination = this.server.getLevel(defaulted); if (playerRespawnDestination == null) { SpongeCommon.logger().warn("The player '{}' respawn location was located in a world that isn't loaded or doesn't exist. This is not safe so " - + "the player will be moved to the spawn of the default world.", player.getGameProfile().getName()); - playerRespawnDestination = player.server.overworld(); + + "the player will be moved to the spawn of the default world.", player.getGameProfile().name()); + playerRespawnDestination = ((ServerPlayerAccessor) player).accessor$server().overworld(); } final RespawnPlayerEvent.SelectWorld event = SpongeEventFactory.createRespawnPlayerEventSelectWorld(PhaseTracker.getInstance().currentCause(), - (ServerWorld) playerRespawnDestination, (ServerWorld) player.serverLevel(), (ServerWorld) playerRespawnDestination, (ServerPlayer) player); + (ServerWorld) playerRespawnDestination, (ServerWorld) player.level(), (ServerWorld) playerRespawnDestination, (ServerPlayer) player); SpongeCommon.post(event); this.impl$respawnLevel = (ServerLevel) event.destinationWorld(); - return ((ServerLevel) event.destinationWorld()).dimension(); + dimension.set(((ServerLevel) event.destinationWorld()).dimension()); + return config; + } + + @WrapOperation( + method = "findRespawnPositionAndUseSpawnBlock", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getLevel(Lnet/minecraft/resources/ResourceKey;)Lnet/minecraft/server/level/ServerLevel;") + ) + private ServerLevel impl$useOverriddenLevel( + final MinecraftServer instance, final ResourceKey key, final Operation original, + @Share("sponge:overridden-respawn") final LocalRef> dimension + ) { + if (dimension.get() != null) { + return original.call(instance, dimension.get()); + } + + return original.call(instance, key); } @Inject(method = "findRespawnPositionAndUseSpawnBlock", at = @At("RETURN")) - private void impl$onFindRespawnPositionAndUseSpawnBlock(final CallbackInfoReturnable cir) { - ((DimensionTransitionAccessor) (Object) cir.getReturnValue()).accessor$newLevel(this.impl$respawnLevel); + private void impl$onFindRespawnPositionAndUseSpawnBlock(final CallbackInfoReturnable cir) { + ((TeleportTransitionAccessor) (Object) cir.getReturnValue()).accessor$newLevel(this.impl$respawnLevel); this.impl$respawnLevel = null; } - @Redirect(method = "addAdditionalSaveData", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hasExactlyOnePlayerPassenger()Z")) + @Redirect(method = "saveParentVehicle", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hasExactlyOnePlayerPassenger()Z")) private boolean impl$skipUnserializableRootVehicle(final Entity instance) { return instance.hasExactlyOnePlayerPassenger() && !((TransientBridge) instance).bridge$isTransient(); } + /** + * Fixes MC-302672 + */ + @WrapOperation(method = "onEffectAdded", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;send(Lnet/minecraft/network/protocol/Packet;)V")) + private void impl$onEffectAdded(ServerGamePacketListenerImpl instance, Packet packet, Operation original) { + if (instance == null) { + return; + } + original.call(instance, packet); + } + @Override public Map bridge$getBedlocations() { return new HashMap<>(this.impl$bedLocations); @@ -1023,17 +868,7 @@ public Entity changeDimension(final DimensionTransition transition) { this.impl$syncingRespawn = true; try { final RespawnLocation active = this.impl$pickActiveRespawn(value); - if (active == null) { - this.shadow$setRespawnPosition(Level.OVERWORLD, null, 0.0F, false, false); - } else { - final ResourceKey dim = ResourceKey.create(Registries.DIMENSION, - (ResourceLocation) (Object) active.worldKey()); - final Vector3d pos = active.position(); - // Vector3d -> BlockPos via floor (BlockPos.containing uses floor semantics) - final BlockPos blockPos = BlockPos.containing(pos.x(), pos.y(), pos.z()); - // RespawnLocation has no yaw; keep the player's current yaw so sleep angle isn't clobbered - this.shadow$setRespawnPosition(dim, blockPos, this.shadow$getYRot(), active.isForced(), false); - } + this.shadow$setRespawnPosition(active == null ? null : this.impl$toRespawnConfig(active), false); } finally { this.impl$syncingRespawn = false; } @@ -1053,7 +888,7 @@ public Entity changeDimension(final DimensionTransition transition) { this.impl$bedLocations.clear(); this.impl$syncingRespawn = true; try { - this.shadow$setRespawnPosition(Level.OVERWORLD, null, 0.0F, false, false); + this.shadow$setRespawnPosition(null, false); } finally { this.impl$syncingRespawn = false; } @@ -1066,12 +901,14 @@ public Entity changeDimension(final DimensionTransition transition) { if (map.isEmpty()) { return null; } - final org.spongepowered.api.ResourceKey vanillaKey = (org.spongepowered.api.ResourceKey) (Object) this.respawnDimension.location(); - final RespawnLocation forVanilla = map.get(vanillaKey); - if (forVanilla != null) { - return forVanilla; + if (this.respawnConfig != null) { + final org.spongepowered.api.ResourceKey vanillaKey = (org.spongepowered.api.ResourceKey) (Object) this.respawnConfig.respawnData().dimension().identifier(); + final RespawnLocation forVanilla = map.get(vanillaKey); + if (forVanilla != null) { + return forVanilla; + } } - final org.spongepowered.api.ResourceKey currentLevelKey = (org.spongepowered.api.ResourceKey) (Object) this.shadow$serverLevel().dimension().location(); + final org.spongepowered.api.ResourceKey currentLevelKey = (org.spongepowered.api.ResourceKey) (Object) this.shadow$level().dimension().identifier(); final RespawnLocation forCurrent = map.get(currentLevelKey); if (forCurrent != null) { return forCurrent; @@ -1079,23 +916,37 @@ public Entity changeDimension(final DimensionTransition transition) { return map.values().iterator().next(); } - @Inject(method = "setRespawnPosition", at = @At("RETURN")) - private void impl$mirrorVanillaRespawnToBedLocations(final ResourceKey dimension, final @Nullable BlockPos pos, - final float angle, final boolean forced, final boolean sendMessage, final CallbackInfo ci) { + @Unique + private net.minecraft.server.level.ServerPlayer.RespawnConfig impl$toRespawnConfig(final RespawnLocation location) { + final ResourceKey dim = ResourceKey.create(Registries.DIMENSION, + (Identifier) (Object) location.worldKey()); + final Vector3d pos = location.position(); + // Vector3d -> BlockPos via floor (BlockPos.containing uses floor semantics). + // RespawnLocation has no yaw/pitch — keep the player's current orientation so sleep angle isn't clobbered. + final LevelData.RespawnData data = LevelData.RespawnData.of( + dim, BlockPos.containing(pos.x(), pos.y(), pos.z()), this.shadow$getYRot(), this.shadow$getXRot()); + return new net.minecraft.server.level.ServerPlayer.RespawnConfig(data, location.isForced()); + } + + @Inject(method = "setRespawnPosition(Lnet/minecraft/server/level/ServerPlayer$RespawnConfig;Z)V", at = @At("RETURN")) + private void impl$mirrorVanillaRespawnToBedLocations( + final @javax.annotation.Nullable net.minecraft.server.level.ServerPlayer.RespawnConfig config, + final boolean sendMessage, final CallbackInfo ci) { if (this.impl$syncingRespawn) { return; } - final org.spongepowered.api.ResourceKey apiKey = (org.spongepowered.api.ResourceKey) (Object) dimension.location(); - if (pos == null) { - // Vanilla clears respawn when pos is null — drop any entry tied to the (now-cleared) dimension - this.impl$bedLocations.remove(apiKey); + if (config == null) { + // Vanilla cleared respawn — drop every entry (vanilla only tracks one slot anyway) + this.impl$bedLocations.clear(); } else { - final RespawnLocation location = RespawnLocation.builder() + final GlobalPos globalPos = config.respawnData().globalPos(); + final BlockPos pos = globalPos.pos(); + final org.spongepowered.api.ResourceKey apiKey = (org.spongepowered.api.ResourceKey) (Object) globalPos.dimension().identifier(); + this.impl$bedLocations.put(apiKey, RespawnLocation.builder() .world(apiKey) .position(new Vector3d(pos.getX() + 0.5D, pos.getY(), pos.getZ() + 0.5D)) - .forceSpawn(forced) - .build(); - this.impl$bedLocations.put(apiKey, location); + .forceSpawn(config.forced()) + .build()); } if (this.impl$bedLocations.isEmpty()) { ((SpongeDataHolderBridge) this).bridge$remove(Keys.RESPAWN_LOCATIONS); @@ -1105,16 +956,17 @@ public Entity changeDimension(final DimensionTransition transition) { } @Inject(method = "readAdditionalSaveData", at = @At("RETURN")) - private void impl$seedBedLocationsFromVanilla(final CompoundTag tag, final CallbackInfo ci) { - if (this.respawnPosition == null || !this.impl$bedLocations.isEmpty()) { + private void impl$seedBedLocationsFromVanilla(final ValueInput input, final CallbackInfo ci) { + if (this.respawnConfig == null || !this.impl$bedLocations.isEmpty()) { return; } - final org.spongepowered.api.ResourceKey apiKey = (org.spongepowered.api.ResourceKey) (Object) this.respawnDimension.location(); - final RespawnLocation location = RespawnLocation.builder() + final GlobalPos globalPos = this.respawnConfig.respawnData().globalPos(); + final BlockPos pos = globalPos.pos(); + final org.spongepowered.api.ResourceKey apiKey = (org.spongepowered.api.ResourceKey) (Object) globalPos.dimension().identifier(); + this.impl$bedLocations.put(apiKey, RespawnLocation.builder() .world(apiKey) - .position(new Vector3d(this.respawnPosition.getX() + 0.5D, this.respawnPosition.getY(), this.respawnPosition.getZ() + 0.5D)) - .forceSpawn(this.respawnForced) - .build(); - this.impl$bedLocations.put(apiKey, location); + .position(new Vector3d(pos.getX() + 0.5D, pos.getY(), pos.getZ() + 0.5D)) + .forceSpawn(this.respawnConfig.forced()) + .build()); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin_Attack_impl.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin_Attack_impl.java deleted file mode 100644 index cb063381820..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin_Attack_impl.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.server.level; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.common.mixin.core.world.entity.player.PlayerMixin_Attack_Impl; -import org.spongepowered.common.util.DamageEventUtil; - -import java.util.ArrayList; - -@Mixin(value = ServerPlayer.class, priority = 900) -public abstract class ServerPlayerMixin_Attack_impl extends PlayerMixin_Attack_Impl { - - @Override - protected void attackImpl$enchanttDamageFunc(Entity $$0, CallbackInfo ci) { - final var weapon = this.attackImpl$attack.weapon(); - // this.getEnchantedDamage(targetEntity, damage, damageSource) - damage; - final var functions = DamageEventUtil.createAttackEnchantmentFunction(weapon, this.attackImpl$attack.target(), this.attackImpl$attack.dmgSource()); - final var separateFunc = DamageEventUtil.provideSeparateEnchantmentFromBaseDamageFunction(this.attackImpl$attack.baseDamage(), weapon); - // enchantmentDamage *= attackStrength; - final var strengthScaleFunc = DamageEventUtil.provideCooldownEnchantmentStrengthFunction(weapon, this.attackImpl$attack.strengthScale()); - - this.attackImpl$attack.functions().addAll(functions); - this.attackImpl$attack.functions().add(separateFunc); - this.attackImpl$attack.functions().add(strengthScaleFunc); - } - - @Override - protected double attackImpl$beforeSweepHurt( - final Player instance, final Entity sweepTarget, final Operation original - ) { - final var distanceToSqr = original.call(instance, sweepTarget); - if (!(distanceToSqr < 9.0)) { - return distanceToSqr; // Too far - no event - } - - final var mainAttack = this.attackImpl$attack; - final var mainAttackDamage = this.attackImpl$finalDamageAmounts.getOrDefault("minecraft:attack_damage", 0.0).floatValue(); - - var sweepAttack = new DamageEventUtil.Attack<>(mainAttack.sourceEntity(), sweepTarget, mainAttack.weapon(), mainAttack.dmgSource(), mainAttack.strengthScale(), 1, new ArrayList<>()); - // float sweepBaseDamage = 1.0F + (float)this.getAttributeValue(Attributes.SWEEPING_DAMAGE_RATIO) * attackDamage; - sweepAttack.functions().add(DamageEventUtil.provideSweepingDamageRatioFunction(mainAttack.weapon(), mainAttack.sourceEntity(), mainAttackDamage)); - // float sweepFullDamage = this.getEnchantedDamage(sweepTarget, sweepBaseDamage, $$3) * strengthScale; - sweepAttack.functions().addAll(DamageEventUtil.createAttackEnchantmentFunction(mainAttack.weapon(), sweepTarget, mainAttack.dmgSource())); - sweepAttack.functions().add(DamageEventUtil.provideCooldownEnchantmentStrengthFunction(mainAttack.weapon(), mainAttack.strengthScale())); - - this.attackImpl$attackEvent = DamageEventUtil.callPlayerAttackEntityEvent(sweepAttack, 1.0F); - if (attackImpl$attackEvent.isCancelled()) { - return Double.MAX_VALUE; - } - - return distanceToSqr; - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/TicketMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/TicketMixin.java index c344c2f7197..81055fd79bc 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/TicketMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/TicketMixin.java @@ -26,13 +26,16 @@ import net.minecraft.server.level.Ticket; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.common.bridge.world.server.TicketBridge; @Mixin(Ticket.class) public abstract class TicketMixin implements TicketBridge { + @Unique private long impl$chunkPosition; - private Ticket impl$parent; + @Unique + private Ticket impl$parent; @Override public long bridge$chunkPosition() { @@ -54,7 +57,7 @@ public abstract class TicketMixin implements TicketBridge { } @Override - public void bridge$setParentTicket(final Ticket parentTicket) { + public void bridge$setParentTicket(final Ticket parentTicket) { this.impl$parent = parentTicket; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/TicketStorageMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/TicketStorageMixin.java new file mode 100644 index 00000000000..eea5de41298 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/TicketStorageMixin.java @@ -0,0 +1,66 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.server.level; + +import net.minecraft.server.level.Ticket; +import net.minecraft.world.level.TicketStorage; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.bridge.world.server.TicketBridge; + +@Mixin(TicketStorage.class) +public class TicketStorageMixin { + + @SuppressWarnings("ConstantConditions") + @Inject(method = "addTicket(JLnet/minecraft/server/level/Ticket;)Z", at = @At("HEAD")) + private void impl$addChunkPosToTicket(final long chunkPos, final net.minecraft.server.level.Ticket ticket, final CallbackInfoReturnable ci) { + ((TicketBridge) (Object) ticket).bridge$setChunkPosition(chunkPos); + } + + @SuppressWarnings("ConstantConditions") + @ModifyVariable(method = "addTicket(JLnet/minecraft/server/level/Ticket;)Z", + at = @At(value = "LOAD"), + slice = @Slice( + from = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/TicketStorage;isTicketSameTypeAndLevel(Lnet/minecraft/server/level/Ticket;Lnet/minecraft/server/level/Ticket;)Z"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/Ticket;resetTicksLeft()V") + ), + index = 6 + ) + private Ticket impl$associateTicketWithParent(final Ticket storedTicket, final long pos, final Ticket originalTicket) { + // We do this because we want to return the original ticket that will actually be operated on, but avoid a + // potentially costly search on an array - because addTicket doesn't return the ticket that is actually + // in the manager. + if (storedTicket != originalTicket) { + ((TicketBridge) (Object) originalTicket).bridge$setParentTicket(storedTicket); + } + return storedTicket; + } + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerCommonPacketListenerImplMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerCommonPacketListenerImplMixin.java index e0386d860a9..4a84163cd65 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerCommonPacketListenerImplMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerCommonPacketListenerImplMixin.java @@ -87,7 +87,7 @@ public abstract class ServerCommonPacketListenerImplMixin implements ServerCommo private Map impl$resourcePackCallbacks = new ConcurrentHashMap<>(); @ModifyVariable( - method = "send(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketSendListener;)V", + method = "send(Lnet/minecraft/network/protocol/Packet;Lio/netty/channel/ChannelFutureListener;)V", at = @At("HEAD"), argsOnly = true ) @@ -108,7 +108,7 @@ public abstract class ServerCommonPacketListenerImplMixin implements ServerCommo @Inject(method = "handleResourcePackResponse", at = @At( value = "INVOKE", - target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/util/thread/BlockableEventLoop;)V", + target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/network/PacketProcessor;)V", shift = At.Shift.AFTER)) private void impl$onHandleResourcePackResponse(final ServerboundResourcePackPacket $$0, final CallbackInfo ci) { final @Nullable ResourcePackInfo pack = this.impl$resourcePackInfos.get($$0.id()); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerConfigurationPacketListenerImplMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerConfigurationPacketListenerImplMixin.java index fbd2b131e31..d215ceecc97 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerConfigurationPacketListenerImplMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerConfigurationPacketListenerImplMixin.java @@ -32,6 +32,7 @@ import net.minecraft.network.protocol.configuration.ServerboundFinishConfigurationPacket; import net.minecraft.server.network.ConfigurationTask; import net.minecraft.server.network.ServerConfigurationPacketListenerImpl; +import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.PlayerList; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.event.SpongeEventFactory; @@ -81,7 +82,7 @@ public abstract class ServerConfigurationPacketListenerImplMixin extends ServerC private boolean impl$skipBanService; @Inject(method = "handleConfigurationFinished", cancellable = true, at = @At(value = "INVOKE", - target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/util/thread/BlockableEventLoop;)V")) + target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/network/PacketProcessor;)V")) private void impl$onHandleConfigurationFinished(final ServerboundFinishConfigurationPacket $$0, final CallbackInfo ci) { if (this.impl$skipBanService) { return; @@ -94,7 +95,7 @@ public abstract class ServerConfigurationPacketListenerImplMixin extends ServerC if (throwable != null) { // An error occurred during login checks so we ask to abort. ((ConnectionBridge) this.connection).bridge$setKickReason(Component.literal("An error occurred checking ban/whitelist status.")); - SpongeCommon.logger().error("An error occurred when checking the ban/whitelist status of {}.", this.gameProfile.getId().toString()); + SpongeCommon.logger().error("An error occurred when checking the ban/whitelist status of {}.", this.gameProfile.id().toString()); SpongeCommon.logger().error(throwable); } else if (componentOpt != null) { // We handle this later @@ -121,7 +122,7 @@ public abstract class ServerConfigurationPacketListenerImplMixin extends ServerC this.impl$skipBanService = true; this.shadow$handleConfigurationFinished($$0); // invalidate just to be sure there is no user cached for the online player anymore - ((SpongeServer) SpongeCommon.server()).userManager().removeFromCache(this.gameProfile.getId()); + ((SpongeServer) SpongeCommon.server()).userManager().removeFromCache(this.gameProfile.id()); } catch (final Exception e) { throw new RuntimeException(e); } finally { @@ -130,15 +131,15 @@ public abstract class ServerConfigurationPacketListenerImplMixin extends ServerC return null; }, SpongeCommon.server()).exceptionally(throwable -> { - SpongeCommon.logger().error("Forcibly disconnecting user {}({}) due to an error during login.", this.gameProfile.getName(), this.gameProfile.getId(), throwable); + SpongeCommon.logger().error("Forcibly disconnecting user {}({}) due to an error during login.", this.gameProfile.name(), this.gameProfile.id(), throwable); this.shadow$disconnect(Component.literal("Internal Server Error: unable to complete login.")); return null; }); } @Redirect(method = "handleConfigurationFinished", - at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;canPlayerLogin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/network/chat/Component;")) - private Component impl$onCanPlayerLogin(final PlayerList instance, final SocketAddress $$0, final GameProfile $$1) { + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;canPlayerLogin(Ljava/net/SocketAddress;Lnet/minecraft/server/players/NameAndId;)Lnet/minecraft/network/chat/Component;")) + private Component impl$onCanPlayerLogin(final PlayerList instance, final SocketAddress $$0, final NameAndId $$1) { //Skip as we check it at the start. return null; } @@ -183,7 +184,7 @@ public Type type() { } @Inject(method = "handleConfigurationFinished", at = @At(value = "INVOKE", - target = "Lnet/minecraft/server/players/PlayerList;getPlayerForLogin(Lcom/mojang/authlib/GameProfile;Lnet/minecraft/server/level/ClientInformation;)Lnet/minecraft/server/level/ServerPlayer;")) + target = "Lnet/minecraft/server/players/PlayerList;getPlayer(Ljava/util/UUID;)Lnet/minecraft/server/level/ServerPlayer;")) private void impl$sendChannels(final CallbackInfo ci) { ((SpongeChannelManager) SpongeCommon.game().channelManager()).sendChannelRegistrations( ((ConnectionBridge) this.connection).bridge$getEngineConnection()); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java index a56141ece5d..f31b3df3c90 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java @@ -42,8 +42,10 @@ import net.minecraft.network.protocol.game.ClientboundMoveVehiclePacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; +import net.minecraft.network.protocol.game.ServerboundAttackPacket; import net.minecraft.network.protocol.game.ServerboundChatCommandSignedPacket; import net.minecraft.network.protocol.game.ServerboundCommandSuggestionPacket; +import net.minecraft.network.protocol.game.ServerboundInteractPacket; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import net.minecraft.network.protocol.game.ServerboundMoveVehiclePacket; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; @@ -56,7 +58,8 @@ import net.minecraft.util.StringUtil; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.RelativeMovement; +import net.minecraft.world.entity.PositionMoveRotation; +import net.minecraft.world.entity.Relative; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.ClipContext; @@ -83,6 +86,7 @@ import org.spongepowered.api.event.block.InteractBlockEvent; import org.spongepowered.api.event.block.entity.ChangeSignEvent; import org.spongepowered.api.event.cause.entity.MovementTypes; +import org.spongepowered.api.event.entity.InteractEntityEvent; import org.spongepowered.api.event.entity.living.AnimateHandEvent; import org.spongepowered.api.event.network.ServerSideConnectionEvent; import org.spongepowered.asm.mixin.Mixin; @@ -117,7 +121,6 @@ import org.spongepowered.math.vector.Vector3d; import java.util.ArrayList; -import java.util.EnumSet; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -133,9 +136,8 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack @Shadow private double vehicleFirstGoodX; @Shadow private double vehicleFirstGoodY; @Shadow private double vehicleFirstGoodZ; - @Shadow private int chatSpamTickCount; - @Shadow public abstract void shadow$teleport(double x, double y, double z, float yaw, float pitch, Set relativeArguments); + @Shadow public abstract void shadow$teleport(PositionMoveRotation pitch, Set relativeArguments); @Shadow protected abstract CompletableFuture> shadow$filterTextPacket(final List $$0); @Shadow protected abstract void shadow$performUnsignedChatCommand(final String $$0); @Shadow protected abstract void shadow$performSignedChatCommand(ServerboundChatCommandSignedPacket $$0, LastSeenMessages $$1); @@ -208,55 +210,63 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack @Inject(method = "handleMovePlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/game/ServerboundMovePlayerPacket;getYRot(F)F"), + slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;hasClientLoaded()Z"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;updateAwaitingTeleport()Z"))) + private void impl$callRotateEntityEvent(final ServerboundMovePlayerPacket packetIn, final CallbackInfo ci) { + if (!packetIn.hasRotation()) { + return; + } + + final ServerPlayer player = (ServerPlayer) this.player; + final Vector3d fromRotation = player.rotation(); + + final Vector3d originalToRotation = new Vector3d(packetIn.getXRot(this.player.getXRot()), + packetIn.getYRot(this.player.getYRot()), 0); + + @Nullable Vector3d toRotation = SpongeCommonEventFactory.callRotateEvent( + player, + fromRotation, + originalToRotation); + + if (toRotation == null) { + toRotation = fromRotation; + } + + if (!toRotation.equals(originalToRotation)) { + this.player.forceSetRotation((float) toRotation.y(), false, (float) toRotation.x(), false); + + ((ServerboundMovePlayerPacketAccessor) packetIn).accessor$yRot((float) toRotation.y()); + ((ServerboundMovePlayerPacketAccessor) packetIn).accessor$xRot((float) toRotation.x()); + ((ServerboundMovePlayerPacketAccessor) packetIn).accessor$hasRot(true); + } + } + + @Inject(method = "handleMovePlayer", + at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/game/ServerboundMovePlayerPacket;getX(D)D"), cancellable = true, slice = @Slice( from = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;updateAwaitingTeleport()Z"), to = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;isPassenger()Z"))) private void impl$callMoveEntityEvent(final ServerboundMovePlayerPacket packetIn, final CallbackInfo ci) { - final boolean fireMoveEvent = packetIn.hasPosition(); - final boolean fireRotationEvent = packetIn.hasRotation(); - - // During login, minecraft sends a packet containing neither the 'moving' or 'rotating' flag set - but only once. - // We don't fire an event to avoid confusing plugins. - if (!fireMoveEvent && !fireRotationEvent) { + if (!packetIn.hasPosition()) { return; } final ServerPlayer player = (ServerPlayer) this.player; final Vector3d fromPosition = player.position(); - final Vector3d fromRotation = player.rotation(); final Vector3d originalToPosition = new Vector3d(packetIn.getX(this.player.getX()), packetIn.getY(this.player.getY()), packetIn.getZ(this.player.getZ())); - final Vector3d originalToRotation = new Vector3d(packetIn.getXRot(this.player.getXRot()), - packetIn.getYRot(this.player.getYRot()), 0); // common checks and throws are done here. final @Nullable Vector3d toPosition; - if (fireMoveEvent) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.NATURAL); - toPosition = SpongeCommonEventFactory.callMoveEvent( - player, - fromPosition, - originalToPosition); - } - } else { - toPosition = originalToPosition; - } - - // Rotation result - Vector3d toRotation; - if (fireRotationEvent) { - toRotation = SpongeCommonEventFactory.callRotateEvent( + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.NATURAL); + toPosition = SpongeCommonEventFactory.callMoveEvent( player, - fromRotation, - originalToRotation); - if (toRotation == null) { - toRotation = fromRotation; - } - } else { - toRotation = originalToRotation; + fromPosition, + originalToPosition); } // At this point, we cancel out and let the "confirmed teleport" code run through to update the @@ -265,14 +275,15 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack // This will both cancel the movement and notify the client about the new rotation if any. // The position is absolute so the momentum will be reset by the client. // The rotation is relative so the head movement is still smooth. - // The client thinks its current rotation is originalToRotation so the new rotation is relative to that. - // The rotation values can be out of "valid" range so set them directly to the same value the client has. - this.player.absMoveTo(fromPosition.x(), fromPosition.y(), fromPosition.z()); - this.player.setXRot((float) originalToRotation.x()); - this.player.setYRot((float) originalToRotation.y()); - this.shadow$teleport(fromPosition.x(), fromPosition.y(), fromPosition.z(), - (float) toRotation.y(), (float) toRotation.x(), - EnumSet.of(RelativeMovement.X_ROT, RelativeMovement.Y_ROT)); + this.player.absSnapTo(fromPosition.x(), fromPosition.y(), fromPosition.z()); + this.shadow$teleport(new PositionMoveRotation( + VecHelper.toVanillaVector3d(fromPosition), + Vec3.ZERO, + 0, + 0 + ), + Relative.ROTATION + ); ci.cancel(); return; } @@ -282,28 +293,15 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack // Notify the client about the new position and new rotation. // Both are relatives so the client will keep its momentum. // The client thinks its current position is originalToPosition so the new position is relative to that. - // The rotation values can be out of "valid" range so set them directly to the same value the client has. - this.player.absMoveTo(originalToPosition.x(), originalToPosition.y(), originalToPosition.z()); - this.player.setXRot((float) originalToRotation.x()); - this.player.setYRot((float) originalToRotation.y()); - this.shadow$teleport(toPosition.x(), toPosition.y(), toPosition.z(), - (float) toRotation.y(), (float) toRotation.x(), - EnumSet.allOf(RelativeMovement.class)); + this.player.absSnapTo(originalToPosition.x(), originalToPosition.y(), originalToPosition.z()); + this.shadow$teleport(new PositionMoveRotation( + VecHelper.toVanillaVector3d(toPosition.sub(originalToPosition)), + Vec3.ZERO, + 0, + 0 + ), + Relative.ALL); ci.cancel(); - } else if (!toRotation.equals(originalToRotation)) { - // Notify the client about the new rotation. - // Both are relatives so the client will keep its momentum. - // The rotation values can be out of "valid" range so set them directly to the same value the client has. - this.player.setXRot((float) originalToRotation.x()); - this.player.setYRot((float) originalToRotation.y()); - this.shadow$teleport(fromPosition.x(), fromPosition.y(), fromPosition.z(), - (float) toRotation.y(), (float) toRotation.x(), - EnumSet.allOf(RelativeMovement.class)); - - // Let MC handle the movement but override the rotation. - ((ServerboundMovePlayerPacketAccessor) packetIn).accessor$yRot((float) toRotation.y()); - ((ServerboundMovePlayerPacketAccessor) packetIn).accessor$xRot((float) toRotation.x()); - ((ServerboundMovePlayerPacketAccessor) packetIn).accessor$hasRot(true); } } @@ -313,7 +311,7 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getControllingPassenger()Lnet/minecraft/world/entity/LivingEntity;") ) private void impl$handleVehicleMoveEvent(final ServerboundMoveVehiclePacket param0, final CallbackInfo ci) { - final ServerboundMoveVehiclePacketAccessor packet = (ServerboundMoveVehiclePacketAccessor) param0; + final ServerboundMoveVehiclePacketAccessor packet = (ServerboundMoveVehiclePacketAccessor) (Object) param0; final Entity rootVehicle = this.player.getRootVehicle(); final Vector3d fromRotation = new Vector3d(rootVehicle.getYRot(), rootVehicle.getXRot(), 0); @@ -321,8 +319,9 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack // We need this because we ignore very small position changes as to not spam as many move events. final Vector3d fromPosition = VecHelper.toVector3d(rootVehicle.position()); - final Vector3d originalToPosition = new Vector3d(param0.getX(), param0.getY(), param0.getZ()); - final Vector3d originalToRotation = new Vector3d(param0.getYRot(), param0.getXRot(), 0); + final var position = param0.position(); + final Vector3d originalToPosition = new Vector3d(position.x, position.y(), position.z()); + final Vector3d originalToRotation = new Vector3d(param0.yRot(), param0.xRot(), 0); // common checks and throws are done here. final @Nullable Vector3d toPosition = SpongeCommonEventFactory.callMoveEvent( @@ -343,22 +342,21 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack if (toPosition == null) { // no point doing all that processing, just account for a potential rotation change. if (!fromRotation.equals(toRotation)) { - rootVehicle.absMoveTo(rootVehicle.getX(), rootVehicle.getY(), rootVehicle.getZ(), (float) toRotation.y(), (float) toRotation.x()); + rootVehicle.absSnapTo(rootVehicle.getX(), rootVehicle.getY(), rootVehicle.getZ(), (float) toRotation.y(), (float) toRotation.x()); } - this.connection.send(new ClientboundMoveVehiclePacket(rootVehicle)); + this.connection.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle)); ci.cancel(); return; } if (!toPosition.equals(originalToPosition) || !toRotation.equals(originalToRotation)) { // notify the client about the new position - rootVehicle.absMoveTo(toPosition.x(), toPosition.y(), toPosition.z(), (float) toRotation.y(), (float) toRotation.x()); - this.connection.send(new ClientboundMoveVehiclePacket(rootVehicle)); + rootVehicle.absSnapTo(toPosition.x(), toPosition.y(), toPosition.z(), (float) toRotation.y(), (float) toRotation.x()); + this.connection.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle)); // update the packet, let MC take care of the rest. - packet.accessor$x(toPosition.x()); - packet.accessor$y(toPosition.y()); - packet.accessor$z(toPosition.z()); + final var newPos = VecHelper.toVanillaVector3d(toPosition); + packet.accessor$position(newPos); packet.accessor$yRot((float) toRotation.x()); packet.accessor$xRot((float) toRotation.y()); @@ -387,7 +385,7 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack if (ShouldFire.INTERACT_ITEM_EVENT_PRIMARY) { final Vec3 startPos = this.player.getEyePosition(1); final Vec3 endPos = startPos.add(this.player.getLookAngle().scale(5d)); // TODO hook for blockReachDistance? - final HitResult result = this.player.serverLevel().clip(new ClipContext(startPos, endPos, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.player)); + final HitResult result = this.player.level().clip(new ClipContext(startPos, endPos, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.player)); if (result.getType() == HitResult.Type.MISS) { final ItemStack heldItem = this.player.getItemInHand(hand); SpongeCommonEventFactory.callInteractItemEventPrimary(this.player, heldItem, hand); @@ -412,7 +410,7 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack } } - @Inject(method = "handlePlayerAction", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;drop(Z)Z")) + @Inject(method = "handlePlayerAction", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;drop(Z)V")) public void impl$dropItem(final ServerboundPlayerActionPacket p_147345_1_, final CallbackInfo ci) { this.impl$ignorePackets++; } @@ -442,7 +440,7 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack return; // prevents Mismatch in destroy block pos warning } } - final PhaseTracker tracker = PhaseTracker.SERVER; + final PhaseTracker tracker = PhaseTracker.getWorldInstance(this.player.level()); try (final CauseStackManager.StackFrame frame = tracker.pushCauseFrame(); final PhaseContext context = PlayerPhase.State.PLAYER_INTERACT.createPhaseContext(tracker) .creator(this.player.getUUID()) @@ -460,6 +458,7 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack @Redirect(method = "removePlayerFromWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Z)V")) + @SuppressWarnings("deprecation") public void impl$handlePlayerDisconnect(final PlayerList instance, final net.minecraft.network.chat.Component $$0, final boolean $$1) { // If this happens, the connection has not been fully established yet so we've kicked them during ClientConnectionEvent.Login, // but FML has created this handler earlier to send their handshake. No message should be sent, no disconnection event should @@ -546,7 +545,7 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack } } - @Redirect(method = "lambda$handleChatCommand$7", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;performUnsignedChatCommand(Ljava/lang/String;)V")) + @Redirect(method = "lambda$handleChatCommand$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;performUnsignedChatCommand(Ljava/lang/String;)V")) public void impl$onPerformChatCommand(final ServerGamePacketListenerImpl instance, final String $$0) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(this.player); @@ -555,7 +554,7 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack } } - @Redirect(method = "lambda$handleSignedChatCommand$8", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;performSignedChatCommand(Lnet/minecraft/network/protocol/game/ServerboundChatCommandSignedPacket;Lnet/minecraft/network/chat/LastSeenMessages;)V")) + @Redirect(method = "lambda$handleSignedChatCommand$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;performSignedChatCommand(Lnet/minecraft/network/protocol/game/ServerboundChatCommandSignedPacket;Lnet/minecraft/network/chat/LastSeenMessages;)V")) public void impl$onPerformSignedChatCommand(final ServerGamePacketListenerImpl instance, final ServerboundChatCommandSignedPacket $$0, final LastSeenMessages $$1) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(this.player); @@ -573,6 +572,38 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack } } + @Inject(method = "handleInteract", at = @At("HEAD"), cancellable = true) + private void impl$throwInteractItemEventForInteract(ServerboundInteractPacket packet, CallbackInfo ci) { + final @Nullable Entity entity = this.player.level().getEntityOrPart(packet.entityId()); + final var hand = packet.hand(); + final var pos = packet.location(); + + final ItemStack itemInHand = hand == null ? ItemStack.EMPTY : player.getItemInHand(hand); + final InteractEntityEvent.Secondary event = SpongeCommonEventFactory + .callInteractEntityEventSecondary(player, itemInHand, entity, hand, VecHelper.toVector3d(pos)); + if (event.isCancelled()) { + ci.cancel(); + } else { + this.bridge$incrementIgnorePackets(); + } + } + + @Inject(method = "handleAttack", at = @At("HEAD"), cancellable = true) + private void impl$throwInteractPrimaryEventForAttack(ServerboundAttackPacket packet, CallbackInfo ci) { + final @Nullable Entity entity = this.player.level().getEntityOrPart(packet.entityId()); + if (entity == null) { + return; + } + + final InteractEntityEvent.Primary event = SpongeCommonEventFactory.callInteractEntityEventPrimary(player, + player.getItemInHand(player.getUsedItemHand()), entity, player.getUsedItemHand()); + if (event.isCancelled()) { + ci.cancel(); + } else { + this.bridge$incrementIgnorePackets(); + } + } + @Override public SpongeTabList bridge$tabList() { return this.impl$tabList; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImpl_1Mixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImpl_1Mixin.java deleted file mode 100644 index bad7f84c38a..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImpl_1Mixin.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.server.network; - -import net.minecraft.network.protocol.game.ServerboundInteractPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.phys.Vec3; -import org.spongepowered.api.event.entity.InteractEntityEvent; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.common.bridge.server.network.ServerGamePacketListenerImplBridge; -import org.spongepowered.common.event.SpongeCommonEventFactory; -import org.spongepowered.common.util.VecHelper; - -@Mixin(targets = "net.minecraft.server.network.ServerGamePacketListenerImpl$1") -public abstract class ServerGamePacketListenerImpl_1Mixin implements ServerboundInteractPacket.Handler { - - @Shadow(aliases = { - // Forge mapped synthetic class member - "val$entity" - }) @Final Entity val$target; - @Shadow @Final ServerGamePacketListenerImpl this$0; - - // Anonymous class - @Inject(method = "onAttack()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;attack(Lnet/minecraft/world/entity/Entity;)V"), cancellable = true) - public void impl$fireLeftClickEvent(final CallbackInfo ci) { - final Entity entity = this.val$target; - final ServerPlayer player = this.this$0.player; - - final InteractEntityEvent.Primary event = SpongeCommonEventFactory.callInteractEntityEventPrimary(player, - player.getItemInHand(player.getUsedItemHand()), entity, player.getUsedItemHand()); - if (event.isCancelled()) { - ci.cancel(); - } else { - ((ServerGamePacketListenerImplBridge) this.this$0).bridge$incrementIgnorePackets(); - } - } - - @Inject(method = "onInteraction(Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/phys/Vec3;)V", at = @At("HEAD"), cancellable = true) - public void impl$fireRightClickEvent(final InteractionHand hand, final Vec3 pos, final CallbackInfo ci) { - final Entity entity = this.val$target; - final ServerPlayer player = this.this$0.player; - - final ItemStack itemInHand = hand == null ? ItemStack.EMPTY : player.getItemInHand(hand); - final InteractEntityEvent.Secondary event = SpongeCommonEventFactory - .callInteractEntityEventSecondary(player, itemInHand, entity, hand, VecHelper.toVector3d(pos)); - if (event.isCancelled()) { - ci.cancel(); - } else { - ((ServerGamePacketListenerImplBridge) this.this$0).bridge$incrementIgnorePackets(); - } - } - -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerLoginPacketListenerImplMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerLoginPacketListenerImplMixin.java index 2de1e77ef32..964eb82b63e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerLoginPacketListenerImplMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerLoginPacketListenerImplMixin.java @@ -37,6 +37,7 @@ import net.minecraft.network.protocol.login.custom.CustomQueryAnswerPayload; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerLoginPacketListenerImpl; +import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.PlayerList; import net.minecraft.util.Crypt; import net.minecraft.util.CryptException; @@ -137,8 +138,8 @@ public abstract class ServerLoginPacketListenerImplMixin implements ServerLoginP } @Redirect(method = "verifyLoginAndFinishConnectionSetup", - at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;canPlayerLogin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/network/chat/Component;")) - private Component impl$onCanPlayerLogin(final PlayerList instance, final SocketAddress $$0, final GameProfile $$1) { + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;canPlayerLogin(Ljava/net/SocketAddress;Lnet/minecraft/server/players/NameAndId;)Lnet/minecraft/network/chat/Component;")) + private Component impl$onCanPlayerLogin(final PlayerList instance, final SocketAddress $$0, final NameAndId $$1) { //We check this as part of auth event. return null; } @@ -172,7 +173,7 @@ public abstract class ServerLoginPacketListenerImplMixin implements ServerLoginP if (throwable != null) { // An error occurred during login checks so we ask to abort. ((ConnectionBridge) this.connection).bridge$setKickReason(Component.literal("An error occurred checking ban/whitelist status.")); - SpongeCommon.logger().error("An error occurred when checking the ban/whitelist status of {}.", this.authenticatedProfile.getId().toString()); + SpongeCommon.logger().error("An error occurred when checking the ban/whitelist status of {}.", this.authenticatedProfile.id().toString()); SpongeCommon.logger().error(throwable); } else if (componentOpt != null) { // We handle this later @@ -191,7 +192,7 @@ public abstract class ServerLoginPacketListenerImplMixin implements ServerLoginP this.impl$fireAuthEvent(); return null; }, ServerLoginPacketListenerImplMixin.impl$EXECUTOR).exceptionally(throwable -> { - SpongeCommon.logger().error("Forcibly disconnecting user {}({}) due to an error during login.", this.authenticatedProfile.getName(), this.authenticatedProfile.getId(), throwable); + SpongeCommon.logger().error("Forcibly disconnecting user {}({}) due to an error during login.", this.authenticatedProfile.name(), this.authenticatedProfile.id(), throwable); this.shadow$disconnect(Component.literal("Internal Server Error: unable to complete login.")); return null; }); @@ -254,10 +255,10 @@ public void handleKey(final ServerboundKeyPacket packet) { final String username = Objects.requireNonNull(this.requestedUsername, "Player name not initialized"); try { - final ProfileResult $$1 = ServerLoginPacketListenerImplMixin.this.server.getSessionService().hasJoinedServer(username, $$5, this.impl$getAddress()); + final ProfileResult $$1 = ServerLoginPacketListenerImplMixin.this.server.services().sessionService().hasJoinedServer(username, $$5, this.impl$getAddress()); if ($$1 != null) { final GameProfile $$2 = $$1.profile(); - ServerLoginPacketListenerImplMixin.LOGGER.info("UUID of player {} is {}", $$2.getName(), $$2.getId()); + ServerLoginPacketListenerImplMixin.LOGGER.info("UUID of player {} is {}", $$2.name(), $$2.id()); ServerLoginPacketListenerImplMixin.this.shadow$startClientVerification($$2); } else if (ServerLoginPacketListenerImplMixin.this.server.isSingleplayer()) { ServerLoginPacketListenerImplMixin.LOGGER.warn("Failed to verify username but will let them in anyway!"); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/config/PrepareSpawnTask_ReadyMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/config/PrepareSpawnTask_ReadyMixin.java new file mode 100644 index 00000000000..a5fa9199cd3 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/config/PrepareSpawnTask_ReadyMixin.java @@ -0,0 +1,98 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.server.network.config; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.world.level.storage.ValueInput; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Slice; + +import java.io.IOException; +import java.time.Instant; +import java.util.Optional; +import java.util.function.Consumer; + +@Mixin(targets = "net.minecraft.server.network.config.PrepareSpawnTask$Ready") +public class PrepareSpawnTask_ReadyMixin { + + @WrapOperation( + method = "spawn", // or the mapped method containing the Player.load call + at = @At( + value = "INVOKE", + target = "Ljava/util/Optional;ifPresent(Ljava/util/function/Consumer;)V" + // If your version uses CompoundTag instead, use the proper descriptor + ) + ) + private void impl$afterPlayerLoad( + final Optional instance, final Consumer action, + final Operation original, + @Local net.minecraft.server.level.ServerPlayer player + ) throws IOException { + // Call vanilla first + original.call(instance, action); + + final ServerPlayer sPlayer = (ServerPlayer) player; + if (sPlayer.get(Keys.FIRST_DATE_JOINED).isEmpty()) { + // TODO - 25w32a changed where we have access to the Player and moved access to the data files elsewhere. +// final Path file = new File(this.playerDir, player.getStringUUID() + ".dat").toPath(); +// final Instant creationTime = java.nio.file.Files.exists(file) +// ? java.nio.file.Files.readAttributes(file, java.nio.file.attribute.BasicFileAttributes.class) +// .creationTime().toInstant() +// : null; +// +// ((SpongeServer) SpongeCommon.server()) +// .getPlayerDataManager() +// .readLegacyPlayerData(sPlayer, nbtOrValueInput, creationTime); + } + + sPlayer.offer(Keys.LAST_DATE_JOINED, java.time.Instant.now()); + } + + + @WrapOperation(method = "spawn", + at = @At(value = "INVOKE", target = "Ljava/util/Optional;ifPresent(Ljava/util/function/Consumer;)V"), + slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;loadPlayerData(Lnet/minecraft/server/players/NameAndId;)Ljava/util/Optional;"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;snapTo(Lnet/minecraft/world/phys/Vec3;FF)V") + ) + ) + private void impl$setPlayerDataForNewPlayers( + final Optional instance, final Consumer action, + final Operation original, + @Local final net.minecraft.server.level.ServerPlayer playerIn + ) { + original.call(instance, action); + if (instance.isEmpty()) { + final Instant now = Instant.now(); + ((ServerPlayer) playerIn).offer(Keys.FIRST_DATE_JOINED, now); + ((ServerPlayer) playerIn).offer(Keys.LAST_DATE_JOINED, now); + } + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/packs/resoruces/MultiPackResourceManagerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/packs/resoruces/MultiPackResourceManagerMixin.java index 4856a0c13ba..e46e602ba35 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/packs/resoruces/MultiPackResourceManagerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/packs/resoruces/MultiPackResourceManagerMixin.java @@ -28,20 +28,32 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.bridge.server.packs.resources.ResourceManagerBridge; +import org.spongepowered.common.service.server.SpongeServerScopedServiceProvider; import org.spongepowered.common.tag.SpongePluginTags; @Mixin(MultiPackResourceManager.class) public abstract class MultiPackResourceManagerMixin implements ResourceManagerBridge { @MonotonicNonNull private SpongePluginTags impl$pluginTags; + @MonotonicNonNull private SpongeServerScopedServiceProvider impl$services; @Override public void bridge$pluginProvidedTags(final SpongePluginTags pluginTags) { this.impl$pluginTags = pluginTags; } + @Override + public void bridge$services(final SpongeServerScopedServiceProvider services) { + this.impl$services = services; + } + @Override public SpongePluginTags bridge$pluginProvidedTags() { return this.impl$pluginTags; } + + @Override + public SpongeServerScopedServiceProvider bridge$services() { + return this.impl$services; + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/players/GameProfileCacheMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/players/GameProfileCacheMixin.java index 443588db5d8..f1dcdc8088e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/players/GameProfileCacheMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/players/GameProfileCacheMixin.java @@ -25,8 +25,8 @@ package org.spongepowered.common.mixin.core.server.players; import com.mojang.authlib.GameProfileRepository; -import com.mojang.authlib.ProfileLookupCallback; -import net.minecraft.server.players.GameProfileCache; +import net.minecraft.server.players.CachedUserNameToIdResolver; +import net.minecraft.server.players.NameAndId; import org.spongepowered.api.Sponge; import org.spongepowered.api.profile.GameProfile; import org.spongepowered.api.profile.GameProfileManager; @@ -49,11 +49,11 @@ import java.util.Optional; import java.util.UUID; -@Mixin(GameProfileCache.class) +@Mixin(CachedUserNameToIdResolver.class) public abstract class GameProfileCacheMixin implements GameProfileCacheBridge { // @formatter:off - @Shadow public void shadow$add(final com.mojang.authlib.GameProfile profile) {} + @Shadow public void shadow$add(final NameAndId profile) {} @Shadow @Final private Map profilesByUUID; @Shadow @Final private Map profilesByName; // @formatter:on @@ -69,7 +69,7 @@ public abstract class GameProfileCacheMixin implements GameProfileCacheBridge { if (accessor.invoker$getExpirationDate().getTime() < System.currentTimeMillis()) { this.profilesByUUID.remove(uniqueId, accessor); - this.profilesByName.remove(accessor.invoker$getProfile().getName(), accessor); + this.profilesByName.remove(accessor.invoker$nameAndId().name(), accessor); return Optional.empty(); } @@ -85,7 +85,7 @@ public abstract class GameProfileCacheMixin implements GameProfileCacheBridge { } if (accessor.invoker$getExpirationDate().getTime() < System.currentTimeMillis()) { - this.profilesByUUID.remove(accessor.invoker$getProfile().getId(), accessor); + this.profilesByUUID.remove(accessor.invoker$nameAndId().id(), accessor); this.profilesByName.remove(lowerName, accessor); return Optional.empty(); } @@ -95,18 +95,19 @@ public abstract class GameProfileCacheMixin implements GameProfileCacheBridge { @Override public void bridge$add(final com.mojang.authlib.GameProfile profile, final boolean full, final boolean signed) { - GameProfileCache_GameProfileInfoAccessor accessor = this.profilesByUUID.get(Objects.requireNonNull(profile, "profile").getId()); - final com.mojang.authlib.GameProfile current = accessor == null ? null : accessor.invoker$getProfile(); + GameProfileCache_GameProfileInfoAccessor accessor = this.profilesByUUID.get(Objects.requireNonNull(profile, "profile").id()); + final NameAndId current = accessor == null ? null : accessor.invoker$nameAndId(); // Don't allow basic game profiles to overwrite the contents if already // an entry exists that is full. - if (current != null && Objects.equals(current.getId(), profile.getId()) && - Objects.equals(current.getName(), profile.getName()) && !full) { + if (current != null && Objects.equals(current.id(), profile.id()) && + Objects.equals(current.name(), profile.name()) && !full) { return; } - this.shadow$add(profile); - accessor = this.profilesByUUID.get(profile.getId()); - if (accessor == null || accessor.invoker$getProfile() != profile) { + final var nameAndIDToAdd = new NameAndId(profile); + this.shadow$add(nameAndIDToAdd); + accessor = this.profilesByUUID.get(profile.id()); + if (accessor == null || accessor.invoker$nameAndId() != nameAndIDToAdd) { return; } final GameProfileCache_GameProfileInfoBridge bridge = (GameProfileCache_GameProfileInfoBridge) accessor; @@ -117,18 +118,18 @@ public abstract class GameProfileCacheMixin implements GameProfileCacheBridge { @Override public void bridge$add(final GameProfile profile, final boolean full, final boolean signed) { GameProfileCache_GameProfileInfoAccessor accessor = this.profilesByUUID.get(Objects.requireNonNull(profile, "profile").uniqueId()); - final com.mojang.authlib.GameProfile current = accessor == null ? null : accessor.invoker$getProfile(); - final com.mojang.authlib.GameProfile mcProfile = SpongeGameProfile.toMcProfile(profile); + final var current = accessor == null ? null : accessor.invoker$nameAndId(); + final var mcProfile = SpongeGameProfile.toNameAndId(profile); // Don't allow basic game profiles to overwrite the contents if already // an entry exists that is full. - if (current != null && Objects.equals(current.getId(), mcProfile.getId()) && - Objects.equals(current.getName(), mcProfile.getName()) && !full) { + if (current != null && Objects.equals(current.id(), mcProfile.id()) && + Objects.equals(current.name(), mcProfile.name()) && !full) { return; } this.shadow$add(mcProfile); accessor = this.profilesByUUID.get(profile.uniqueId()); - if (accessor == null || accessor.invoker$getProfile() != mcProfile) { + if (accessor == null || accessor.invoker$nameAndId() != mcProfile) { return; } ((GameProfileCache_GameProfileInfoBridge) accessor).bridge$set(profile, full, signed); @@ -140,27 +141,20 @@ public abstract class GameProfileCacheMixin implements GameProfileCacheBridge { } @Inject(method = "add", at = @At(value = "RETURN")) - private void impl$updateCacheUsername(final com.mojang.authlib.GameProfile profile, final CallbackInfo ci) { - if (profile.getName() != null) { - ((SpongeServer) Sponge.server()).getUsernameCache().setUsername(profile.getId(), profile.getName()); + private void impl$updateCacheUsername(final NameAndId profile, final CallbackInfo ci) { + if (!profile.name().isEmpty()) { + ((SpongeServer) Sponge.server()).getUsernameCache().setUsername(profile.id(), profile.name()); } } - @Redirect(method = "lookupGameProfile", - at = @At( - value = "INVOKE", - target = "Lcom/mojang/authlib/GameProfileRepository;findProfilesByNames([Ljava/lang/String;Lcom/mojang/authlib/ProfileLookupCallback;)V", - remap = false - ) - ) - private static void impl$lookUpViaSponge(final GameProfileRepository repository, final String[] names, - final ProfileLookupCallback callback) { + @Redirect(method = "lookupGameProfile", at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/GameProfileRepository;findProfileByName(Ljava/lang/String;)Ljava/util/Optional;")) + private static Optional impl$lookUpViaSponge(final GameProfileRepository instance, final String name) { final GameProfileManager profileManager = Sponge.server().gameProfileManager(); try { - callback.onProfileLookupSucceeded(SpongeGameProfile.toMcProfile(profileManager.basicProfile(names[0]).join())); + final var join = profileManager.basicProfile(name).join(); + return Optional.of(new com.mojang.authlib.yggdrasil.response.NameAndId(join.uuid(), join.name().orElse(""))); } catch (final Throwable e) { - callback.onProfileLookupFailed(names[0], - e instanceof final Exception ex ? ex : new RuntimeException(e)); + return Optional.empty(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/players/GameProfileCache_GameProfileInfoMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/players/GameProfileCache_GameProfileInfoMixin.java index 5a11c591048..5a29e3c28a7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/players/GameProfileCache_GameProfileInfoMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/players/GameProfileCache_GameProfileInfoMixin.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.mixin.core.server.players; +import net.minecraft.server.players.NameAndId; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.profile.GameProfile; import org.spongepowered.asm.mixin.Final; @@ -32,10 +33,10 @@ import org.spongepowered.common.bridge.server.players.GameProfileCache_GameProfileInfoBridge; import org.spongepowered.common.profile.SpongeGameProfile; -@Mixin(targets = "net.minecraft.server.players.GameProfileCache$GameProfileInfo") +@Mixin(targets = "net/minecraft/server/players/CachedUserNameToIdResolver$GameProfileInfo") public abstract class GameProfileCache_GameProfileInfoMixin implements GameProfileCache_GameProfileInfoBridge { - @Shadow @Final private com.mojang.authlib.GameProfile profile; + @Shadow @Final private NameAndId nameAndId; private boolean impl$isFull; private boolean impl$isSigned; @@ -75,7 +76,7 @@ public abstract class GameProfileCache_GameProfileInfoMixin implements GameProfi if (basic != null) { return basic; } - basic = this.impl$basic = SpongeGameProfile.basicOf(this.profile); + basic = this.impl$basic = SpongeGameProfile.basicOf(this.nameAndId); return basic; } @@ -92,11 +93,11 @@ public abstract class GameProfileCache_GameProfileInfoMixin implements GameProfi if (!this.impl$isSigned) { return null; } - full = this.impl$fullSigned = SpongeGameProfile.of(this.profile); + full = this.impl$fullSigned = SpongeGameProfile.of(this.nameAndId); } else { final GameProfile fullSigned = this.impl$fullSigned; full = this.impl$fullUnsigned = SpongeGameProfile.unsignedOf( - fullSigned == null ? SpongeGameProfile.of(this.profile) : fullSigned); + fullSigned == null ? SpongeGameProfile.of(this.nameAndId) : fullSigned); } return full; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/players/PlayerListMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/players/PlayerListMixin.java index 372a26352a0..fae0502b2d9 100755 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/players/PlayerListMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/players/PlayerListMixin.java @@ -28,6 +28,7 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalRef; @@ -38,7 +39,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; import net.minecraft.network.Connection; import net.minecraft.network.chat.ChatType; import net.minecraft.network.chat.MutableComponent; @@ -47,14 +47,15 @@ import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.common.ClientboundDisconnectPacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerScoreboard; import net.minecraft.server.bossevents.CustomBossEvents; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.network.CommonListenerCookie; import net.minecraft.server.players.IpBanList; +import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.PlayerList; import net.minecraft.server.players.UserBanList; import net.minecraft.server.players.UserWhiteList; @@ -62,7 +63,7 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.border.BorderChangeListener; import net.minecraft.world.level.border.WorldBorder; -import net.minecraft.world.level.portal.DimensionTransition; +import net.minecraft.world.level.portal.TeleportTransition; import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.Opcodes; import org.slf4j.Logger; @@ -156,13 +157,11 @@ public abstract class PlayerListMixin implements PlayerListBridge { @Shadow @Final @Mutable private IpBanList ipBans; @Shadow @Final @Mutable private UserWhiteList whitelist; @Shadow @Final private List players; - @Shadow @Final protected int maxPlayers; @Shadow @Final private Map playersByUUID; - @Shadow public abstract MinecraftServer shadow$getServer(); - @Shadow public abstract Optional shadow$load(final net.minecraft.server.level.ServerPlayer $$0); - @Shadow public abstract boolean shadow$canBypassPlayerLimit(com.mojang.authlib.GameProfile param0); + @Shadow public abstract boolean shadow$canBypassPlayerLimit(NameAndId $$0); @Shadow protected abstract boolean shadow$verifyChatTrusted(final PlayerChatMessage $$0); + @Shadow public abstract int shadow$getMaxPlayers(); @Shadow protected abstract void shadow$save(net.minecraft.server.level.ServerPlayer $$0); // @formatter:on @@ -182,10 +181,10 @@ public abstract class PlayerListMixin implements PlayerListBridge { if (this instanceof IntegratedPlayerListBridge) { return ((IntegratedPlayerListBridge) this).bridge$canPlayerLoginClient(param0, param1); } - return this.impl$canPlayerLoginServer(param0, param1); + return this.impl$canPlayerLoginServer(param0, new NameAndId(param1)); } - protected final CompletableFuture impl$canPlayerLoginServer(final SocketAddress param0, final com.mojang.authlib.GameProfile param1) { + protected final CompletableFuture impl$canPlayerLoginServer(final SocketAddress param0, final NameAndId param1) { final SpongeGameProfile profile = SpongeGameProfile.basicOf(param1); return Sponge.server().serviceProvider().banService().find(profile).thenCompose(profileBanOpt -> { @@ -226,7 +225,7 @@ public abstract class PlayerListMixin implements PlayerListBridge { return CompletableFuture.completedFuture(null); } final PermissionService permissionService = Sponge.server().serviceProvider().permissionService(); - return permissionService.userSubjects().loadSubject(param1.getId().toString()).handle((subject, ex) -> { + return permissionService.userSubjects().loadSubject(param1.id().toString()).handle((subject, ex) -> { if (ex == null) { return subject.hasPermission(LoginPermissions.BYPASS_WHITELIST_PERMISSION); } @@ -248,35 +247,23 @@ public abstract class PlayerListMixin implements PlayerListBridge { if (component != null) { return component; } - if (this.players.size() >= this.maxPlayers && !this.shadow$canBypassPlayerLimit(param1)) { + if (this.players.size() >= this.shadow$getMaxPlayers() && !this.shadow$canBypassPlayerLimit(param1)) { return net.minecraft.network.chat.Component.translatable("multiplayer.disconnect.server_full"); } return null; }, SpongeCommon.server()); } - @Redirect(method = "placeNewPlayer", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/server/players/PlayerList;load(Lnet/minecraft/server/level/ServerPlayer;)Ljava/util/Optional;" - ) - ) - private Optional impl$setPlayerDataForNewPlayers(final PlayerList playerList, final net.minecraft.server.level.ServerPlayer playerIn) { - final Optional compound = this.shadow$load(playerIn); - if (compound.isEmpty()) { - final Instant now = Instant.now(); - ((ServerPlayer) playerIn).offer(Keys.FIRST_DATE_JOINED, now); - ((ServerPlayer) playerIn).offer(Keys.LAST_DATE_JOINED, now); - } - return compound; - } - @Redirect(method = "placeNewPlayer", + @WrapOperation(method = "placeNewPlayer", at = @At(value = "INVOKE", - target = "Lnet/minecraft/server/MinecraftServer;getLevel(Lnet/minecraft/resources/ResourceKey;)Lnet/minecraft/server/level/ServerLevel;" + target = "Lnet/minecraft/server/level/ServerPlayer;level()Lnet/minecraft/server/level/ServerLevel;" ) ) - private net.minecraft.server.level.ServerLevel impl$onInitPlayer_getWorld(final MinecraftServer minecraftServer, - final ResourceKey dimension, final Connection networkManager, final net.minecraft.server.level.ServerPlayer mcPlayer + private net.minecraft.server.level.ServerLevel impl$onInitPlayer_getWorld( + final net.minecraft.server.level.ServerPlayer instance, final Operation original, + final Connection networkManager, final net.minecraft.server.level.ServerPlayer mcPlayer, + final CommonListenerCookie ci ) { final net.minecraft.network.chat.@Nullable Component kickReason = ((ConnectionBridge) networkManager).bridge$getKickReason(); final Component disconnectMessage; @@ -286,13 +273,13 @@ public abstract class PlayerListMixin implements PlayerListBridge { disconnectMessage = Component.text("You are not allowed to log in to this server."); } - net.minecraft.server.level.ServerLevel mcWorld = minecraftServer.getLevel(dimension); + net.minecraft.server.level.ServerLevel mcWorld = original.call(instance); if (mcWorld == null) { SpongeCommon.logger().warn("The player '{}' was located in a world that isn't loaded or doesn't exist. This is not safe so " - + "the player will be moved to the spawn of the default world.", mcPlayer.getGameProfile().getName()); - mcWorld = minecraftServer.overworld(); - final BlockPos spawnPoint = mcWorld.getSharedSpawnPos(); + + "the player will be moved to the spawn of the default world.", mcPlayer.getGameProfile().name()); + mcWorld = this.server.overworld(); + final BlockPos spawnPoint = mcWorld.getRespawnData().pos(); mcPlayer.setPos(spawnPoint.getX() + 0.5, spawnPoint.getY() + 0.5, spawnPoint.getZ() + 0.5); } @@ -309,7 +296,7 @@ public abstract class PlayerListMixin implements PlayerListBridge { final Cause cause = Cause.of(EventContext.empty(), connection, user); final ServerSideConnectionEvent.Login event = SpongeEventFactory.createServerSideConnectionEventLogin(cause, disconnectMessage, - disconnectMessage, location, location, rotation, rotation, connection, SpongeGameProfile.of(mcPlayer.getGameProfile()), user); + disconnectMessage, location, location, rotation, rotation, connection, SpongeGameProfile.of(mcPlayer.getGameProfile()), user); if (kickReason != null) { event.setCancelled(true); } @@ -320,16 +307,18 @@ public abstract class PlayerListMixin implements PlayerListBridge { final ServerLocation toLocation = event.toLocation(); final Vector3d toRotation = event.toRotation(); - mcPlayer.absMoveTo(toLocation.x(), toLocation.y(), toLocation.z(), - (float) toRotation.y(), (float) toRotation.x()); - return (net.minecraft.server.level.ServerLevel) toLocation.world(); + mcPlayer.absSnapTo(toLocation.x(), toLocation.y(), toLocation.z(), + (float) toRotation.y(), (float) toRotation.x()); + final var level = (ServerLevel) toLocation.world(); + instance.setServerLevel(level); + return level; } @Inject(method = "placeNewPlayer", cancellable = true, at = @At( value = "INVOKE", - target = "Lnet/minecraft/server/MinecraftServer;getLevel(Lnet/minecraft/resources/ResourceKey;)Lnet/minecraft/server/level/ServerLevel;", + target = "Lnet/minecraft/server/level/ServerPlayer;level()Lnet/minecraft/server/level/ServerLevel;", shift = At.Shift.AFTER ) ) @@ -346,9 +335,9 @@ public abstract class PlayerListMixin implements PlayerListBridge { ) ) private void impl$onInitPlayer_printPlayerWorldInJoinFeedback(final org.slf4j.Logger logger, final String s, final Object[] objects, - final Connection conn, final net.minecraft.server.level.ServerPlayer player) { + final Connection conn, final net.minecraft.server.level.ServerPlayer player) { logger.info("{}[{}] logged in to world '{}' with entity id {} at ({}, {}, {})", player.getName().getString(), player, - ((ServerWorld)player.serverLevel()).key(), player.getId(), player.getX(), player.getY(), player.getZ()); + ((ServerWorld) player.level()).key(), player.getId(), player.getX(), player.getY(), player.getZ()); } @Redirect(method = "placeNewPlayer", @@ -382,26 +371,26 @@ public abstract class PlayerListMixin implements PlayerListBridge { ) ) private void impl$onInitPlayer_delaySendMessage(final PlayerList instance, final net.minecraft.network.chat.Component message, - final boolean $$1, final Connection manager, final net.minecraft.server.level.ServerPlayer playerIn + final boolean $$1, final Connection manager, final net.minecraft.server.level.ServerPlayer playerIn ) { // Don't send here, will be done later. We cache the expected message. ((ServerPlayerBridge) playerIn).bridge$setConnectionMessageToSend(message); } - @Redirect(method = "placeNewPlayer", at = @At(value = "FIELD", target = "Lnet/minecraft/server/players/PlayerList;viewDistance:I")) + @Redirect(method = "placeNewPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;getViewDistance()I")) private int impl$usePerWorldViewDistance(final PlayerList self, final Connection co, final net.minecraft.server.level.ServerPlayer player, final CommonListenerCookie cookie) { - return ((ServerLevelDataBridge) player.serverLevel().getLevelData()).bridge$viewDistance().orElse(self.getViewDistance()); + return ((ServerLevelDataBridge) player.level().getLevelData()).bridge$viewDistance().orElse(self.getViewDistance()); } @Redirect(method = "placeNewPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getCustomBossEvents()Lnet/minecraft/server/bossevents/CustomBossEvents;")) private CustomBossEvents impl$getPerWorldBossBarManager( - final MinecraftServer minecraftServer, final Connection netManager, final net.minecraft.server.level.ServerPlayer playerIn) { - return ((ServerLevelBridge) playerIn.serverLevel()).bridge$getBossBarManager(); + final MinecraftServer minecraftServer, final Connection netManager, final net.minecraft.server.level.ServerPlayer playerIn) { + return ((ServerLevelBridge) playerIn.level()).bridge$getBossBarManager(); } @Redirect(method = "placeNewPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;updateEntireScoreboard(Lnet/minecraft/server/ServerScoreboard;Lnet/minecraft/server/level/ServerPlayer;)V")) private void impl$sendScoreboard(final PlayerList playerList, final ServerScoreboard scoreboardIn, final net.minecraft.server.level.ServerPlayer playerIn) { - ((ServerPlayerBridge)playerIn).bridge$initScoreboard(); + ((ServerPlayerBridge) playerIn).bridge$initScoreboard(); } @Redirect( @@ -412,7 +401,7 @@ public abstract class PlayerListMixin implements PlayerListBridge { ) ) private void impl$onlySendSelfAddPlayerOnVanished(final PlayerList playerList, final Packet addPlayer, - final Connection playerConnection, final net.minecraft.server.level.ServerPlayer serverPlayer + final Connection playerConnection, final net.minecraft.server.level.ServerPlayer serverPlayer ) { if (((VanishableBridge) serverPlayer).bridge$vanishState().hidesTabListEntry()) { serverPlayer.connection.send(addPlayer); @@ -436,6 +425,7 @@ public abstract class PlayerListMixin implements PlayerListBridge { } @Inject(method = "placeNewPlayer", at = @At(value = "RETURN")) + @SuppressWarnings("deprecation") private void impl$onInitPlayer_join(final Connection networkManager, final net.minecraft.server.level.ServerPlayer mcPlayer, final CommonListenerCookie $$2, final CallbackInfo ci) { final ServerPlayer player = (ServerPlayer) mcPlayer; final ServerSideConnection connection = player.connection(); @@ -444,7 +434,7 @@ public abstract class PlayerListMixin implements PlayerListBridge { final Component joinComponent = SpongeAdventure.asAdventure(((ServerPlayerBridge) mcPlayer).bridge$getConnectionMessageToSend()); final ServerSideConnectionEvent.Join event = SpongeEventFactory.createServerSideConnectionEventJoin(cause, audience, - Optional.of(audience), joinComponent, joinComponent, connection, player, SpongeGameProfile.of(mcPlayer.getGameProfile()), false); + Optional.of(audience), joinComponent, joinComponent, connection, player, SpongeGameProfile.of(mcPlayer.getGameProfile()), false); SpongeCommon.post(event); if (!event.isMessageCancelled()) { event.audience().ifPresent(audience1 -> audience1.sendMessage(Identity.nil(), event.message())); @@ -452,8 +442,9 @@ public abstract class PlayerListMixin implements PlayerListBridge { ((ServerPlayerBridge) mcPlayer).bridge$setConnectionMessageToSend(null); - final PhaseContext context = PhaseTracker.SERVER.getPhaseContext(); - PhaseTracker.SERVER.pushCause(event); + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(mcPlayer.level()); + final PhaseContext context = phaseTracker.getPhaseContext(); + phaseTracker.pushCause(event); final TransactionalCaptureSupplier transactor = context.getTransactor(); transactor.logPlayerInventoryChange(mcPlayer, PlayerInventoryTransaction.EventCreator.STANDARD); try (final EffectTransactor ignored = BroadcastInventoryChangesEffect.transact(transactor)) { @@ -463,7 +454,7 @@ public abstract class PlayerListMixin implements PlayerListBridge { @Redirect(method = "remove", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getCustomBossEvents()Lnet/minecraft/server/bossevents/CustomBossEvents;")) private CustomBossEvents impl$getPerWorldBossBarManager(final MinecraftServer minecraftServer, final net.minecraft.server.level.ServerPlayer playerIn) { - return ((ServerLevelBridge) playerIn.serverLevel()).bridge$getBossBarManager(); + return ((ServerLevelBridge) playerIn.level()).bridge$getBossBarManager(); } @Inject(method = "remove", at = @At("HEAD")) @@ -483,31 +474,14 @@ public abstract class PlayerListMixin implements PlayerListBridge { worldBorder.addListener(new PerWorldBorderListener(serverWorld)); } - @Redirect(method = "load", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/level/ServerPlayer;load(Lnet/minecraft/nbt/CompoundTag;)V" - ) - ) - private void impl$setSpongePlayerDataForSinglePlayer(final net.minecraft.server.level.ServerPlayer entity, final CompoundTag compound) { - entity.load(compound); - - if (((ServerPlayer) entity).get(Keys.FIRST_DATE_JOINED).isEmpty()) { - ((SpongeServer) this.shadow$getServer()).getPlayerDataManager().readLegacyPlayerData((ServerPlayer) entity, compound, null); - } - - ((ServerPlayer) entity).offer(Keys.LAST_DATE_JOINED, Instant.now()); - } - - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Inject(method = "respawn", at = @At(value = "INVOKE", - target = "Lnet/minecraft/server/level/ServerPlayer;findRespawnPositionAndUseSpawnBlock(ZLnet/minecraft/world/level/portal/DimensionTransition$PostDimensionTransition;)Lnet/minecraft/world/level/portal/DimensionTransition;" + target = "Lnet/minecraft/server/level/ServerPlayer;findRespawnPositionAndUseSpawnBlock(ZLnet/minecraft/world/level/portal/TeleportTransition$PostTeleportTransition;)Lnet/minecraft/world/level/portal/TeleportTransition;" ) ) private void impl$flagIfRespawnPositionIsGameMechanic(final net.minecraft.server.level.ServerPlayer $$0, final boolean $$1, - final Entity.RemovalReason $$2, final CallbackInfoReturnable cir) { - this.impl$isRespawnWithPosition = $$0.getRespawnPosition() != null; + final Entity.RemovalReason $$2, final CallbackInfoReturnable cir) { + this.impl$isRespawnWithPosition = $$0.getRespawnConfig() != null; } @Redirect( @@ -515,11 +489,11 @@ public abstract class PlayerListMixin implements PlayerListBridge { at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;getX()D"), slice = @Slice( from = @At(value = "NEW", target = "(Lnet/minecraft/network/protocol/game/CommonPlayerSpawnInfo;B)Lnet/minecraft/network/protocol/game/ClientboundRespawnPacket;"), - to = @At(value = "NEW", target = "(Lnet/minecraft/core/BlockPos;F)Lnet/minecraft/network/protocol/game/ClientboundSetDefaultSpawnPositionPacket;") + to = @At(value = "NEW", target = "(Lnet/minecraft/world/level/storage/LevelData$RespawnData;)Lnet/minecraft/network/protocol/game/ClientboundSetDefaultSpawnPositionPacket;") ) ) private double impl$callRespawnPlayerRecreateEvent(final net.minecraft.server.level.ServerPlayer newPlayer, - final net.minecraft.server.level.ServerPlayer player, final boolean keepAllPlayerData, final @Local DimensionTransition dimensionTransition) { + final net.minecraft.server.level.ServerPlayer player, final boolean keepAllPlayerData, final @Local TeleportTransition dimensionTransition) { final ServerPlayer originalPlayer = (ServerPlayer) player; final ServerPlayer recreatedPlayer = (ServerPlayer) newPlayer; @@ -529,9 +503,9 @@ public abstract class PlayerListMixin implements PlayerListBridge { final ServerWorld destinationWorld = recreatedPlayer.world(); final RespawnPlayerEvent.Recreate event = SpongeEventFactory.createRespawnPlayerEventRecreate(PhaseTracker.getInstance().currentCause(), - destinationPosition, originalWorld, originalPosition, destinationWorld, - (ServerWorld) dimensionTransition.newLevel(), - destinationPosition, originalPlayer, recreatedPlayer, this.impl$isRespawnWithPosition, !keepAllPlayerData); + destinationPosition, originalWorld, originalPosition, destinationWorld, + (ServerWorld) dimensionTransition.newLevel(), + destinationPosition, originalPlayer, recreatedPlayer, this.impl$isRespawnWithPosition, !keepAllPlayerData); SpongeCommon.post(event); this.impl$isRespawnWithPosition = false; @@ -558,27 +532,21 @@ public abstract class PlayerListMixin implements PlayerListBridge { @Inject(method = "respawn", at = @At("RETURN")) private void impl$callRespawnPlayerPostEvent(final net.minecraft.server.level.ServerPlayer player, final boolean $$1, final Entity.RemovalReason $$2, - final CallbackInfoReturnable cir, final @Local DimensionTransition dimensionTransition) { + final CallbackInfoReturnable cir, final @Local TeleportTransition dimensionTransition) { final ServerPlayer recreatedPlayer = (ServerPlayer) cir.getReturnValue(); - final ServerWorld originalWorld = (ServerWorld) player.serverLevel(); + final ServerWorld originalWorld = (ServerWorld) player.level(); final RespawnPlayerEvent.Post event = SpongeEventFactory.createRespawnPlayerEventPost(PhaseTracker.getInstance().currentCause(), - recreatedPlayer.world(), originalWorld, (ServerWorld) dimensionTransition.newLevel(), recreatedPlayer); + recreatedPlayer.world(), originalWorld, (ServerWorld) dimensionTransition.newLevel(), recreatedPlayer); SpongeCommon.post(event); } - @Redirect(method = "sendLevelInfo", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;overworld()Lnet/minecraft/server/level/ServerLevel;")) - private ServerLevel impl$usePerWorldWorldBorder(final MinecraftServer minecraftServer, final net.minecraft.server.level.ServerPlayer playerIn, - final ServerLevel worldIn) { - return worldIn; - } - private void impl$disconnectClient(final Connection netManager, final Component disconnectMessage, final @Nullable GameProfile profile) { final net.minecraft.network.chat.Component reason = SpongeAdventure.asVanilla(disconnectMessage); try { - PlayerListMixin.LOGGER.info("Disconnecting " + (profile != null ? profile.toString() + " (" + netManager.getRemoteAddress().toString() + ")" : - netManager.getRemoteAddress() + ": " + reason.getString())); + PlayerListMixin.LOGGER.info("Disconnecting " + (profile != null ? profile + " (" + netManager.getRemoteAddress() + ")" : + netManager.getRemoteAddress() + ": " + reason.getString())); netManager.send(new ClientboundDisconnectPacket(reason)); netManager.disconnect(reason); } catch (final Exception exception) { @@ -592,9 +560,9 @@ public abstract class PlayerListMixin implements PlayerListBridge { } @Inject(method = "broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Ljava/util/function/Function;Z)V", - at = @At("HEAD"), cancellable = true) + at = @At("HEAD"), cancellable = true) private void impl$onBroadcastSystemMessage(final net.minecraft.network.chat.Component $$0, - final Function $$1, final boolean $$2, final CallbackInfo ci) { + final Function $$1, final boolean $$2, final CallbackInfo ci) { if (this.impl$isDuringSystemMessageEvent) { return; } @@ -603,7 +571,7 @@ public abstract class PlayerListMixin implements PlayerListBridge { final Audience originalAudience = (Audience) this.server; final Component originalMessage = SpongeAdventure.asAdventure($$0); final SystemMessageEvent event = SpongeEventFactory.createSystemMessageEvent(PhaseTracker.getInstance().currentCause(), - originalAudience, Optional.of(originalAudience), originalMessage, originalMessage); + originalAudience, Optional.of(originalAudience), originalMessage, originalMessage); if (SpongeCommon.post(event)) { ci.cancel(); return; @@ -627,7 +595,7 @@ public abstract class PlayerListMixin implements PlayerListBridge { final Registry chatTypeRegistry = SpongeCommon.vanillaRegistry(Registries.CHAT_TYPE); final var chatType = RegistryTypes.CHAT_TYPE.defaultReferenced((org.spongepowered.api.ResourceKey) (Object) chatTypeRegistry.getKey(boundChatType.chatType().value())); - try (final CauseStackManager.StackFrame frame = PhaseTracker.SERVER.pushCauseFrame()) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getServerInstanceExplicitly().pushCauseFrame()) { if (player != null) { frame.pushCause(player); } @@ -638,8 +606,8 @@ public abstract class PlayerListMixin implements PlayerListBridge { return; } - boundChatType = ChatType.bind(ResourceKey.create(Registries.CHAT_TYPE, (ResourceLocation) (Object) event.chatType().location()), - this.server.registryAccess(), SpongeAdventure.asVanilla(event.sender())); + boundChatType = ChatType.bind(ResourceKey.create(Registries.CHAT_TYPE, (Identifier) (Object) event.chatType().location()), + this.server.registryAccess(), SpongeAdventure.asVanilla(event.sender())); if (event.target().isPresent()) { boundChatType = boundChatType.withTargetName(SpongeAdventure.asVanilla(event.target().get())); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/service/permission/SubjectMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/service/permission/SubjectMixin.java index cf03857795d..111c2e8f0bb 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/service/permission/SubjectMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/service/permission/SubjectMixin.java @@ -26,7 +26,7 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.rcon.RconConsoleSource; -import net.minecraft.world.entity.vehicle.MinecartCommandBlock; +import net.minecraft.world.entity.vehicle.minecart.MinecartCommandBlock; import net.minecraft.world.level.block.entity.CommandBlockEntity; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.service.permission.Subject; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/tags/TagKeyMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/tags/TagKeyMixin.java new file mode 100644 index 00000000000..0320278cc14 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/tags/TagKeyMixin.java @@ -0,0 +1,38 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.tags; + +import net.minecraft.tags.TagKey; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.bridge.tags.TagBridge; + +@Mixin(TagKey.class) +public abstract class TagKeyMixin implements TagBridge { + + @Override + public TagKey bridge$asVanillaTag() { + return (TagKey) (Object) this; + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/tags/TagLoaderMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/tags/TagLoaderMixin.java index d49b6084873..6b7897c054d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/tags/TagLoaderMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/tags/TagLoaderMixin.java @@ -24,9 +24,12 @@ */ package org.spongepowered.common.mixin.core.tags; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.core.Holder; +import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.tags.TagEntry; import net.minecraft.tags.TagLoader; @@ -47,6 +50,7 @@ import org.spongepowered.common.bridge.server.packs.resources.ResourceManagerBridge; import org.spongepowered.common.bridge.tags.TagLoaderBridge; import org.spongepowered.common.bridge.tags.TagLoader_EntryWithSourceBridge; +import org.spongepowered.common.launch.Launch; import org.spongepowered.common.tag.SpongePluginTagModifier; import org.spongepowered.common.tag.SpongePluginTagPredicate; import org.spongepowered.common.tag.SpongePluginTags; @@ -64,20 +68,20 @@ public abstract class TagLoaderMixin implements TagLoaderBridge { private @MonotonicNonNull RegistryType impl$registryType; private @MonotonicNonNull Map> impl$modifiers; - private ResourceLocation impl$buildingTagKey; + private Identifier impl$buildingTagKey; @SuppressWarnings({"unchecked", "rawtypes"}) @Inject(method = "load", at = @At("TAIL")) - private void impl$onLoad(final ResourceManager $$0, final CallbackInfoReturnable>> cir) { + private void impl$onLoad(final ResourceManager $$0, final CallbackInfoReturnable>> cir) { final SpongePluginTags tags = ((ResourceManagerBridge) $$0).bridge$pluginProvidedTags(); tags.get(this.impl$registryType).ifPresent(m -> { this.impl$modifiers = (Map) m; m.forEach((tagKey, modifiers) -> { - final List entries = cir.getReturnValue().computeIfAbsent((ResourceLocation) (Object) tagKey, $ -> new ArrayList<>()); + final List entries = cir.getReturnValue().computeIfAbsent((Identifier) (Object) tagKey, $ -> new ArrayList<>()); modifiers.append().forEach((k, v) -> { final TagLoader.EntryWithSource entry = new TagLoader.EntryWithSource(k.tag() - ? TagEntry.optionalTag((ResourceLocation) (Object) k.key()) - : TagEntry.optionalElement((ResourceLocation) (Object) k.key()), "sponge"); + ? TagEntry.optionalTag((Identifier) (Object) k.key()) + : TagEntry.optionalElement((Identifier) (Object) k.key()), "sponge"); ((TagLoader_EntryWithSourceBridge) (Object) entry).bridge$predicates((Set) v); entries.add(entry); }); @@ -121,7 +125,7 @@ public abstract class TagLoaderMixin implements TagLoaderBridge { final Tag tag = Tag.of(this.impl$registryType, (ResourceKey) (Object) ((TagEntryAccessor) e.entry()).accessor$id()); for (final T value : values) { - final ResourceKey key = (ResourceKey) (Object) ((Holder.Reference) value).key().location(); + final ResourceKey key = (ResourceKey) (Object) ((Holder.Reference) value).key().identifier(); final DefaultedRegistryReference reference = RegistryKey.of(this.impl$registryType, key).asDefaultedReference(Sponge::game); for (final SpongePluginTagPredicate predicate : filters) { if (predicate.apply(reference, tag) == Tristate.TRUE) { @@ -143,7 +147,7 @@ public abstract class TagLoaderMixin implements TagLoaderBridge { } else if (((TagEntryAccessor) instance).accessor$tag()) { final Tag tag = Tag.of(this.impl$registryType, (ResourceKey) (Object) ((TagEntryAccessor) instance).accessor$id()); return original.accept(instance, lookup, i -> { - final ResourceKey key = (ResourceKey) (Object) ((Holder.Reference) i).key().location(); + final ResourceKey key = (ResourceKey) (Object) ((Holder.Reference) i).key().identifier(); final DefaultedRegistryReference reference = RegistryKey.of(this.impl$registryType, key).asDefaultedReference(Sponge::game); for (final SpongePluginTagPredicate predicate : modifier.filters()) { if (predicate.apply(reference, tag) == Tristate.FALSE) { @@ -177,14 +181,39 @@ public abstract class TagLoaderMixin implements TagLoaderBridge { return false; } + @SuppressWarnings("unchecked") + @WrapOperation(method = "loadTagsForRegistry(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/tags/TagLoader$ElementLookup;)Ljava/util/Map;", at = @At(value = "NEW", target = "Lnet/minecraft/tags/TagLoader;")) + private static TagLoader impl$onLoadTagsForRegistry( + final TagLoader.ElementLookup elementLookup, final String directory, final Operation original, + final ResourceManager resourceManager, final net.minecraft.resources.ResourceKey> registryKey, final TagLoader.ElementLookup> lookup + ) { + final TagLoader loader = original.call(elementLookup, directory); + ((TagLoaderBridge) loader).bridge$registryKey(registryKey); + return loader; + } + + @SuppressWarnings("unchecked") + @WrapOperation(method = "loadPendingTags", at = @At(value = "NEW", target = "Lnet/minecraft/tags/TagLoader;")) + private static TagLoader impl$onLoadPendingTags(final TagLoader.ElementLookup $$0, final String $$1, final Operation> original, + final ResourceManager resourceManager, final Registry registry) { + final TagLoader loader = original.call($$0, $$1); + ((TagLoaderBridge) loader).bridge$registryKey(registry.key()); + return loader; + } + + @Inject(method = "loadTagsForExistingRegistries", at = @At("HEAD")) + private static void impl$onLoadTagsForExistingRegistries(final ResourceManager $$0, final RegistryAccess $$1, final CallbackInfoReturnable>> cir) { + Launch.instance().lifecycle().establishTags($$0); + } + @Override - public void bridge$registryEntry(final RegistryAccess.RegistryEntry registryEntry) { + public void bridge$registryKey(final net.minecraft.resources.ResourceKey> registryKey) { this.impl$registryType = - RegistryType.of((ResourceKey) (Object) registryEntry.key().registry(), (ResourceKey) (Object) registryEntry.key().location()); + RegistryType.of((ResourceKey) (Object) registryKey.registry(), (ResourceKey) (Object) registryKey.identifier()); } @Override - public void bridge$buildingTagKey(final @Nullable ResourceLocation key) { + public void bridge$buildingTagKey(final @Nullable Identifier key) { this.impl$buildingTagKey = key; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/tags/TagManagerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/tags/TagManagerMixin.java deleted file mode 100644 index 7613b6ad443..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/tags/TagManagerMixin.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.tags; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.PreparableReloadListener; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.tags.TagLoader; -import net.minecraft.tags.TagManager; -import net.minecraft.util.profiling.ProfilerFiller; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.common.bridge.tags.TagLoaderBridge; -import org.spongepowered.common.launch.Launch; - -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.Function; - -@Mixin(TagManager.class) -public abstract class TagManagerMixin { - - @Inject(method = "reload", at = @At("HEAD")) - private void impl$onReload(final PreparableReloadListener.PreparationBarrier $$0, final ResourceManager $$1, final ProfilerFiller $$2, final ProfilerFiller $$3, final Executor $$4, final Executor $$5, final CallbackInfoReturnable> cir) { - Launch.instance().lifecycle().establishTags($$1); - } - - @SuppressWarnings("unchecked") - @WrapOperation(method = "createLoader", at = @At(value = "NEW", target = "Lnet/minecraft/tags/TagLoader;")) - private TagLoader impl$onCreateLoader(final Function> $$0, final String $$1, final Operation> original, - final ResourceManager resourceManager, Executor executor, RegistryAccess.RegistryEntry registryEntry) { - final TagLoader loader = original.call($$0, $$1); - ((TagLoaderBridge) loader).bridge$registryEntry(registryEntry); - return loader; - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/util/filefix/FileFixerUpperMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/util/filefix/FileFixerUpperMixin.java new file mode 100644 index 00000000000..dcee32e25ec --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/util/filefix/FileFixerUpperMixin.java @@ -0,0 +1,211 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.util.filefix; + +import com.mojang.logging.LogUtils; +import com.mojang.serialization.Dynamic; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtIo; +import net.minecraft.resources.ResourceKey; +import net.minecraft.util.filefix.FileFixerUpper; +import net.minecraft.util.worldupdate.UpgradeProgress; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.storage.LevelStorageSource; +import org.apache.commons.io.file.PathUtils; +import org.slf4j.Logger; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.world.server.SpongeLevelMigration; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Mixin on {@link FileFixerUpper} to preserve Sponge's per-world identity (UUID, key) + * during vanilla's file-fixing process, which rewrites {@code level.dat} and strips + * unknown tags including the {@code SpongeData} compound. + * + *

This also cleans up old Sponge-created files ({@code DIM-1/level.dat}, + * {@code DIM1/level.dat}, {@code session.lock}) so that vanilla's + * {@code DimensionStorageFileFix} can fully delete the legacy directories.

+ */ +@Mixin(FileFixerUpper.class) +public abstract class FileFixerUpperMixin { + + @Unique private static final Logger SPONGE_LOGGER = LogUtils.getLogger(); + + // TODO: Also cache MapUUIDs and player-uuid-table from old SpongeData for migration + // to separate SavedData files (sponge:map_uuids, etc.) — see follow-up task + + /** + * Extracts SpongeData from level.dat and per-dimension level.dat files BEFORE + * vanilla's file fixer rewrites them. Also cleans up old Sponge files so + * vanilla can fully delete DIM-1/DIM1 directories. + */ + @Inject(method = "fix(Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;Lcom/mojang/serialization/Dynamic;Lnet/minecraft/util/worldupdate/UpgradeProgress;)Lcom/mojang/serialization/Dynamic;", + at = @At("HEAD")) + private void sponge$extractSpongeDataBeforeFix( + final LevelStorageSource.LevelStorageAccess worldAccess, + final Dynamic levelDataTag, + final UpgradeProgress upgradeProgress, + final CallbackInfoReturnable> cir + ) { + final Map, UUID> identities = new HashMap<>(); + final Path worldDir = worldAccess.getLevelDirectory().path(); + + // 1. Extract overworld UUID from root level.dat's SpongeData (in the Dynamic) + // Note: levelDataTag is the "Data" compound, but SpongeData is a SIBLING of Data + // at the root level. We need to re-read the raw file to get it. + try { + final CompoundTag root = NbtIo.readCompressed(worldDir.resolve("level.dat"), NbtAccounter.defaultQuota()); + final CompoundTag spongeData = root.getCompound("SpongeData").orElse(null); + if (spongeData != null && !spongeData.isEmpty()) { + sponge$extractUuid(spongeData, Level.OVERWORLD, identities); + // Also cache MapUUIDs and player-uuid-table for global migration + SpongeLevelMigration.cacheGlobalSpongeData(spongeData); + } + } catch (final IOException e) { + // No level.dat or can't read — skip + } + + // 2. Extract nether/end UUIDs from their per-dimension level.dat files + // and clean up old Sponge files so vanilla can delete the directories + sponge$extractAndCleanDimension(worldDir, "DIM-1", Level.NETHER, identities); + sponge$extractAndCleanDimension(worldDir, "DIM1", Level.END, identities); + + if (!identities.isEmpty()) { + SPONGE_LOGGER.info("[Sponge] Preserved {} world UUID(s) from old SpongeData during file fix", identities.size()); + // Cache for writing AFTER the fix completes and the COW swap is done. + // The actual write happens in MinecraftServerMixin.loadLevel() via SpongeLevelMigration. + SpongeLevelMigration.cacheIdentities(identities); + } + } + + // Registry.dat writing is deferred to MinecraftServerMixin.loadLevel() HEAD inject, + // which runs AFTER fix() completes (COW swap done) but BEFORE any ServerLevel is created. + + /** + * Extracts a UUID from a SpongeData compound's "UUID" IntArray field. + */ + @Unique + private static void sponge$extractUuid( + final CompoundTag spongeData, + final ResourceKey dimKey, + final Map, UUID> identities + ) { + final int[] uuidArray = spongeData.getIntArray("UUID").orElse(null); + if (uuidArray != null && uuidArray.length == 4) { + final UUID uuid = new UUID( + (long) uuidArray[0] << 32 | uuidArray[1] & 0xFFFFFFFFL, + (long) uuidArray[2] << 32 | uuidArray[3] & 0xFFFFFFFFL + ); + identities.put(dimKey, uuid); + } + } + + /** + * Reads SpongeData from an old per-dimension level.dat (e.g. DIM-1/level.dat), + * extracts the UUID, then deletes all Sponge-created files so vanilla's + * DimensionStorageFileFix can fully remove the legacy directory. + */ + @Unique + private static void sponge$extractAndCleanDimension( + final Path worldDir, + final String legacyDirName, + final ResourceKey dimKey, + final Map, UUID> identities + ) { + final Path legacyDir = worldDir.resolve(legacyDirName); + final Path legacyLevelDat = legacyDir.resolve("level.dat"); + + if (!Files.exists(legacyLevelDat)) { + return; + } + + // Extract SpongeData + try { + final CompoundTag root = NbtIo.readCompressed(legacyLevelDat, NbtAccounter.defaultQuota()); + final CompoundTag spongeData = root.getCompound("SpongeData").orElse(null); + if (spongeData != null && !spongeData.isEmpty()) { + sponge$extractUuid(spongeData, dimKey, identities); + } + } catch (final IOException e) { + SPONGE_LOGGER.warn("[Sponge] Failed to read SpongeData from {}", legacyLevelDat, e); + } + + // Migrate any remaining SavedData files that vanilla didn't move + // (e.g. random_sequences.dat that was left behind in DIM-1/data/) + // Old format: DIM-1/data/random_sequences.dat (flat, no namespace) + // New format: dimensions/minecraft/the_nether/data/minecraft/random_sequences.dat (namespaced) + final Path legacyDataDir = legacyDir.resolve("data"); + final Path newDimDir = DimensionType.getStorageFolder(dimKey, worldDir); + final Path newDataDir = newDimDir.resolve("data").resolve("minecraft"); // namespace subdirectory + try { + if (Files.exists(legacyDataDir) && Files.isDirectory(legacyDataDir)) { + Files.createDirectories(newDataDir); + try (var stream = Files.list(legacyDataDir)) { + stream.filter(Files::isRegularFile).forEach(source -> { + final Path target = newDataDir.resolve(source.getFileName()); + try { + if (!Files.exists(target)) { + Files.move(source, target); + SPONGE_LOGGER.info("[Sponge] Migrated {} -> {}", source.getFileName(), target); + } + } catch (final IOException e) { + SPONGE_LOGGER.warn("[Sponge] Failed to migrate {}", source, e); + } + }); + } + } + } catch (final IOException e) { + SPONGE_LOGGER.warn("[Sponge] Failed to migrate data files from {}", legacyDirName, e); + } + + // Clean up old Sponge files so vanilla can delete the legacy directory + try { + Files.deleteIfExists(legacyLevelDat); + Files.deleteIfExists(legacyDir.resolve("session.lock")); + if (Files.exists(legacyDataDir)) { + PathUtils.deleteDirectory(legacyDataDir); + } + // Delete the now-empty legacy directory + if (Files.exists(legacyDir) && Files.list(legacyDir).findFirst().isEmpty()) { + Files.delete(legacyDir); + } + SPONGE_LOGGER.info("[Sponge] Cleaned up legacy directory {}", legacyDirName); + } catch (final IOException e) { + SPONGE_LOGGER.warn("[Sponge] Failed to clean up {}", legacyDirName, e); + } + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/BaseCommandBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/BaseCommandBlockMixin.java index da95a861ee5..105a33efbe6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/BaseCommandBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/BaseCommandBlockMixin.java @@ -25,33 +25,22 @@ package org.spongepowered.common.mixin.core.world; import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.audience.MessageType; -import net.kyori.adventure.identity.Identity; -import net.kyori.adventure.text.Component; +import net.minecraft.commands.CommandSource; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.BaseCommandBlock; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.event.Cause; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.commands.CommandSourceProviderBridge; +import javax.annotation.Nullable; + @Mixin(BaseCommandBlock.class) public abstract class BaseCommandBlockMixin implements CommandSourceProviderBridge, Audience { // @formatter:off - @Shadow public abstract CommandSourceStack shadow$createCommandSourceStack(); - @Shadow public abstract void shadow$sendSystemMessage(net.minecraft.network.chat.Component $$0); + @Shadow public abstract CommandSourceStack createCommandSourceStack(ServerLevel level, CommandSource var1); + @Shadow @Nullable protected abstract BaseCommandBlock.CloseableCommandBlockSource shadow$createSource(ServerLevel level); // @formatter:on - @Override - public CommandSourceStack bridge$getCommandSource(final Cause cause) { - return this.shadow$createCommandSourceStack(); - } - - @Override - public void sendMessage(final @NonNull Identity identity, final @NonNull Component message, final @NonNull MessageType type) { - this.shadow$sendSystemMessage(SpongeAdventure.asVanilla(message)); - } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/damagesource/DamageSourcesMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/damagesource/DamageSourcesMixin.java index 097b90cd9c2..9d4bc671a77 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/damagesource/DamageSourcesMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/damagesource/DamageSourcesMixin.java @@ -31,7 +31,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.accessor.world.level.ExplosionAccessor; +import org.spongepowered.common.accessor.world.level.ServerExplosionAccessor; import org.spongepowered.common.bridge.CreatorTrackedBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; @@ -45,7 +45,7 @@ private LivingEntity onSetExplosionSource(final Explosion explosion) { // When indirect source is not set if (explosion.getIndirectSourceEntity() == null && explosion.getDirectSourceEntity() instanceof CreatorTrackedBridge creatorBridge - && !((LevelBridge) ((ExplosionAccessor) explosion).accessor$level()).bridge$isFake()) { + && !((LevelBridge) ((ServerExplosionAccessor) explosion).accessor$level()).bridge$isFake()) { // check creator var indirectSource = creatorBridge.tracker$getCreatorUUID().flatMap(x -> Sponge.server().player(x)); if (indirectSource.isPresent()) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/AvatarMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/AvatarMixin.java new file mode 100644 index 00000000000..3ab7f182324 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/AvatarMixin.java @@ -0,0 +1,40 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity; + +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Avatar; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(Avatar.class) +public abstract class AvatarMixin extends LivingEntityMixin { + + //@formatter:off + @Shadow @Final protected static EntityDataAccessor DATA_PLAYER_MODE_CUSTOMISATION; + //@formatter:on + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMaxAirMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMaxAirMixin.java index 2a8f7da99c2..8fc76c652fb 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMaxAirMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMaxAirMixin.java @@ -25,7 +25,7 @@ package org.spongepowered.common.mixin.core.world.entity; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.animal.Dolphin; +import net.minecraft.world.entity.animal.dolphin.Dolphin; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java index 1353e67e01b..d8e28c532cd 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java @@ -26,9 +26,13 @@ import com.google.common.collect.ImmutableList; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Cancellable; import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -36,6 +40,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; +import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -46,8 +51,9 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.LightningBolt; +import net.minecraft.world.entity.InsideBlockEffectApplier; import net.minecraft.world.entity.PortalProcessor; +import net.minecraft.world.entity.Relative; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; @@ -57,7 +63,10 @@ import net.minecraft.world.level.block.Portal; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.GameEvent; -import net.minecraft.world.level.portal.DimensionTransition; +import net.minecraft.world.level.portal.TeleportTransition; +import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; @@ -88,10 +97,10 @@ import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -99,6 +108,7 @@ import org.spongepowered.common.accessor.server.level.ChunkMapAccessor; import org.spongepowered.common.accessor.server.level.ChunkMap_TrackedEntityAccessor; import org.spongepowered.common.accessor.world.entity.PortalProcessorAccessor; +import org.spongepowered.common.accessor.world.level.storage.TagValueInputAccessor; import org.spongepowered.common.bridge.commands.CommandSourceProviderBridge; import org.spongepowered.common.bridge.data.DataCompoundHolder; import org.spongepowered.common.bridge.data.SpongeDataHolderBridge; @@ -115,7 +125,8 @@ import org.spongepowered.common.event.ShouldFire; import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.hooks.PlatformHooks; +import org.spongepowered.common.event.tracking.phase.entity.EntityPhase; +import org.spongepowered.common.event.tracking.phase.entity.TeleportContext; import org.spongepowered.common.item.util.ItemStackUtil; import org.spongepowered.common.util.Constants; import org.spongepowered.common.util.DamageEventUtil; @@ -123,11 +134,7 @@ import org.spongepowered.common.util.VecHelper; import org.spongepowered.math.vector.Vector3d; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; +import java.util.*; @Mixin(Entity.class) public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, VanishableBridge, CommandSourceProviderBridge, DataCompoundHolder, TransientBridge { @@ -156,15 +163,14 @@ public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, @Shadow public abstract void shadow$discard(); @Shadow public abstract boolean shadow$isRemoved(); @Shadow public abstract int shadow$getId(); - @Shadow public abstract boolean shadow$isVehicle(); @Shadow public abstract void shadow$playSound(SoundEvent soundIn, float volume, float pitch); @Shadow public abstract boolean shadow$isInvisible(); @Shadow public abstract void shadow$setInvisible(boolean invisible); @Shadow public abstract EntityType shadow$getType(); @Shadow public abstract void shadow$teleportTo(double x, double y, double z); - @Shadow public abstract CommandSourceStack shadow$createCommandSourceStack(); + @Shadow public abstract CommandSourceStack shadow$createCommandSourceStackForNameResolution(ServerLevel level); @Shadow public abstract net.minecraft.world.phys.Vec3 shadow$position(); - @Shadow @Nullable public abstract ItemEntity shadow$spawnAtLocation(ItemStack stack, float offsetY); + @Shadow @Nullable public abstract ItemEntity shadow$spawnAtLocation(ServerLevel $$0, ItemStack stack, float offsetY); @Shadow @Nullable public abstract Entity shadow$getVehicle(); @Shadow public abstract AABB shadow$getBoundingBox(); @Shadow @Nullable public abstract PlayerTeam shadow$getTeam(); @@ -172,8 +178,7 @@ public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, @Shadow public abstract net.minecraft.world.phys.Vec3 shadow$getDeltaMovement(); @Shadow public abstract void shadow$setDeltaMovement(net.minecraft.world.phys.Vec3 motion); @Shadow public abstract void shadow$unRide(); - @Shadow protected abstract void shadow$removeAfterChangingDimensions(); - @Shadow protected abstract int shadow$getPermissionLevel(); + @Shadow public abstract boolean shadow$teleportTo(ServerLevel level, double x, double y, double z, Set relatives, float xRot, float yRot, boolean setCamera); @Shadow public abstract float shadow$getYRot(); @Shadow public abstract float shadow$getXRot(); @Shadow public abstract void shadow$setYRot(final float param0); @@ -182,12 +187,15 @@ public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, @Shadow @Nullable protected abstract String shadow$getEncodeId(); @Shadow @javax.annotation.Nullable public PortalProcessor portalProcess; @Shadow public abstract void shadow$stopRiding(); + @Shadow public abstract Level level(); // @formatter:on + + private boolean impl$isConstructing = true; private VanishState impl$vanishState = VanishState.unvanished(); protected boolean impl$transient = false; - protected boolean impl$moveEventsFired = false; + protected boolean impl$skipTeleportCrossDimensionBefore = false; protected boolean impl$hasCustomFireImmuneTicks = false; protected short impl$fireImmuneTicks = 0; private BlockPos impl$lastCollidedBlockPos; @@ -228,80 +236,20 @@ public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, return false; } - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) this.shadow$level()); + try (final CauseStackManager.StackFrame frame = phaseTracker.pushCauseFrame()) { frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.PLUGIN); - - final ServerLevel originalWorld = (ServerLevel) this.shadow$level(); - final ServerLevel originalDestinationWorld = (ServerLevel) location.world(); - final ServerLevel destinationWorld; - final @org.checkerframework.checker.nullness.qual.Nullable Vector3d destinationPosition; - - final boolean isChangeOfWorld = this.shadow$level() != originalDestinationWorld; - if (isChangeOfWorld) { - final ChangeEntityWorldEvent.Pre event = PlatformHooks.INSTANCE.getEventHooks() - .callChangeEntityWorldEventPre((Entity) (Object) this, originalDestinationWorld); - if (event.isCancelled() || ((LevelBridge) event.destinationWorld()).bridge$isFake()) { - return false; - } - - destinationWorld = (ServerLevel) event.destinationWorld(); - final ChangeEntityWorldEvent.Reposition repositionEvent = - this.bridge$fireRepositionEvent(event.originalDestinationWorld(), event.destinationWorld(), location.position()); - if (repositionEvent.isCancelled()) { - return false; - } - destinationPosition = repositionEvent.destinationPosition(); - } else { - destinationWorld = (ServerLevel) this.shadow$level(); - destinationPosition = this.impl$fireMoveEvent(PhaseTracker.SERVER, location.position()); - if (destinationPosition == null) { - return false; - } - } - - final boolean completed = this.impl$setLocation(isChangeOfWorld, destinationWorld, destinationPosition); - - if (isChangeOfWorld) { - Sponge.eventManager().post(SpongeEventFactory.createChangeEntityWorldEventPost( - PhaseTracker.getInstance().currentCause(), - (org.spongepowered.api.entity.Entity) this, - (ServerWorld) originalWorld, - (ServerWorld) originalDestinationWorld, - (ServerWorld) destinationWorld - )); - } - return completed; + return this.shadow$teleportTo((ServerLevel) location.world(), location.x(), location.y(), location.z(), Relative.ROTATION, 0, 0, true); } } - /** - * {@link Entity#teleportTo(double, double, double)} - * {@link Entity#teleportTo(ServerLevel, double, double, double, Set, float, float)} - */ - protected boolean impl$setLocation(final boolean isChangeOfWorld, final ServerLevel level, final Vector3d pos) { - // TODO post teleport ticket needed? - - if (!isChangeOfWorld) { - this.shadow$teleportTo(pos.x(), pos.y(), pos.z()); - return true; - } - - this.shadow$unRide(); - /*TODO old code had: - this.bridge$remove(Entity.RemovalReason.CHANGED_DIMENSION, true); // but via PlatformServerLevelBridge#bridge$removeEntity - this.bridge$revive(); - level.addDuringTeleport((Entity) (Object) this); - */ - var entity = this.shadow$getType().create(level); - if (entity == null) { - return false; + @WrapMethod(method = "teleport") + private Entity impl$wrapTeleport(final TeleportTransition transition, Operation original) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(this.shadow$level()); + try (final CauseStackManager.StackFrame frame = phaseTracker.pushCauseFrame()) { + frame.pushCause(this); + return original.call(transition); } - entity.restoreFrom((Entity) (Object) this); - entity.moveTo(pos.x(), pos.y(), pos.z()); - this.shadow$setRemoved(Entity.RemovalReason.CHANGED_DIMENSION); - level.addDuringTeleport(entity); - // TODO old code had reset empty time? on original/new world - return true; } @Override @@ -408,18 +356,7 @@ public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, @Override public CommandSourceStack bridge$getCommandSource(final Cause cause) { - return this.shadow$createCommandSourceStack(); - } - - /** - * @author faithcaio - * @reason handle dimension change events see {@link #bridge$changeDimension(DimensionTransition)} - * - * TODO we may need to separate into Vanilla and Forge again - */ - @Overwrite - public Entity changeDimension(final DimensionTransition transition) { - return this.bridge$changeDimension(transition); + return this.shadow$createCommandSourceStackForNameResolution((ServerLevel) this.shadow$level()); } @Inject(method = "setAsInsidePortal", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, shift = At.Shift.AFTER, @@ -459,8 +396,8 @@ public Entity changeDimension(final DimensionTransition transition) { * See {@link PortalProcessorMixin#impl$onGetPortalDestination} for portal events */ @Redirect(method = "handlePortal", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;changeDimension(Lnet/minecraft/world/level/portal/DimensionTransition;)Lnet/minecraft/world/entity/Entity;")) - public Entity impl$onChangeDimension(final Entity instance, final DimensionTransition transition) { + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;teleport(Lnet/minecraft/world/level/portal/TeleportTransition;)Lnet/minecraft/world/entity/Entity;")) + public Entity impl$onChangeDimension(final Entity instance, final TeleportTransition transition) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(this); var be = this.shadow$level().getBlockEntity(this.portalProcess.getEntryPosition()); @@ -474,88 +411,205 @@ public Entity changeDimension(final DimensionTransition transition) { frame.addContext(EventContextKeys.PORTAL_LOGIC, (PortalLogic) portal); // TODO frame.addContext(EventContextKeys.PORTAL, transition); // TODO 2-dim portal? - this.impl$moveEventsFired = true; - return instance.changeDimension(transition); + this.impl$skipTeleportCrossDimensionBefore = true; + return instance.teleport(transition); } finally { - this.impl$moveEventsFired = false; + this.impl$skipTeleportCrossDimensionBefore = false; } } - /** - * This is effectively an overwrite of changeDimension. - * - * @return The {@link Entity} that is either this one, or replaces this one - */ - @SuppressWarnings("ConstantConditions") - @Nullable - public Entity bridge$changeDimension(final DimensionTransition transition) { - final Entity thisEntity = (Entity) (Object) this; - if (!(thisEntity.level() instanceof ServerLevel oldLevel) || this.shadow$isRemoved()) { // Sponge inverted check - return null; + @ModifyVariable(method = "teleportCrossDimension", at = @At("HEAD"), argsOnly = true) + private TeleportTransition impl$beforeTeleportCrossDimension(TeleportTransition transition, @Cancellable final CallbackInfoReturnable cir) { + transition = this.impl$fireTeleportCrossDimensionBefore(transition); + if (transition == null) { + cir.setReturnValue(null); } + return transition; + } - // TODO context/events for non-players - var newLevel = transition.newLevel(); - var passengers = thisEntity.getPassengers(); - List recreatedPassengers = new ArrayList<>(); - this.shadow$unRide(); - for (final Entity passenger : passengers) { - var recreatedPassenger = passenger.changeDimension(transition); - if (recreatedPassenger != null) { - recreatedPassengers.add(recreatedPassenger); - } + @ModifyVariable(method = "teleportCrossDimension", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getPassengers()Ljava/util/List;"), argsOnly = true, ordinal = 1) + private ServerLevel impl$useChangeWorldEventLevel(final ServerLevel originalNewLevel, @Local(argsOnly = true) final TeleportTransition transition, + @Share("original-new-level") final LocalRef originalNewLevelRef) { + originalNewLevelRef.set(originalNewLevel); + return transition.newLevel(); + } + + @Inject(method = "teleportCrossDimension", at = @At("RETURN")) + private void impl$afterTeleportCrossDimension(final ServerLevel level, final ServerLevel newLevel, final TeleportTransition transition, + final CallbackInfoReturnable cir, @Share("original-new-level") final LocalRef originalNewLevelRef) { + final Entity newEntity = cir.getReturnValue(); + ((EntityMixin) (Object) newEntity).impl$fireTeleportCrossDimensionAfter(level, newLevel, originalNewLevelRef.get()); + } + + + @ModifyVariable(method = "teleportSameDimension", at = @At("HEAD"), argsOnly = true) + private TeleportTransition impl$beforeTeleportSameDimension(TeleportTransition transition, @Cancellable final CallbackInfoReturnable cir) { + transition = this.impl$fireTeleportSameDimension(transition); + if (transition == null) { + cir.setReturnValue(null); + } + return transition; + } + + protected final @Nullable TeleportTransition impl$fireTeleportCrossDimensionBefore(TeleportTransition transition) { + if (this.impl$skipTeleportCrossDimensionBefore) { + return transition; } - oldLevel.getProfiler().push("changeDimension"); + final ServerLevel originalLevel = (ServerLevel) this.shadow$level(); + final ServerLevel originalDestinationLevel = transition.newLevel(); + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(originalLevel); + try (final TeleportContext context = EntityPhase.State.PORTAL_DIMENSION_CHANGE.createPhaseContext(phaseTracker).worldChange()) { + context.buildAndSwitch(); - var newEntity = oldLevel.dimension() == newLevel.dimension() ? thisEntity : thisEntity.getType().create(newLevel); - if (newEntity != null) { - if (thisEntity != newEntity) { - newEntity.restoreFrom(thisEntity); - this.shadow$removeAfterChangingDimensions(); + final ChangeEntityWorldEvent.Pre preEvent = SpongeEventFactory.createChangeEntityWorldEventPre( + phaseTracker.currentCause(), + (org.spongepowered.api.entity.Entity) this, + (ServerWorld) originalLevel, + (ServerWorld) originalDestinationLevel, + (ServerWorld) originalDestinationLevel); + if (SpongeCommon.post(preEvent)) { + return null; } - newEntity.moveTo(transition.pos().x, transition.pos().y, transition.pos().z, transition.yRot(), newEntity.getXRot()); - newEntity.setDeltaMovement(transition.speed()); - if (thisEntity != newEntity) { - newLevel.addDuringTeleport(newEntity); + if (preEvent.destinationWorld() != originalDestinationLevel) { + transition = new TeleportTransition( + (ServerLevel) preEvent.destinationWorld(), + transition.position(), + transition.deltaMovement(), + transition.yRot(), transition.xRot(), + transition.missingRespawnBlock(), + transition.asPassenger(), + transition.relatives(), + transition.postTeleportTransition() + ); } - for (Entity recreatedPassenger : recreatedPassengers) { - recreatedPassenger.startRiding(newEntity, true); + final Vector3d originalDestination = this.impl$absoluteDestinationPosition(transition); + final ChangeEntityWorldEvent.Reposition repositionEvent = SpongeEventFactory.createChangeEntityWorldEventReposition( + phaseTracker.currentCause(), + (org.spongepowered.api.entity.Entity) this, + (ServerWorld) originalLevel, + VecHelper.toVector3d(this.position), + originalDestination, + (ServerWorld) originalDestinationLevel, + originalDestination, + (ServerWorld) transition.newLevel() + ); + transition = this.impl$fireMove(transition, repositionEvent); + if (transition == null) { + return null; } - oldLevel.resetEmptyTime(); - newLevel.resetEmptyTime(); - transition.postDimensionTransition().onTransition(newEntity); + return this.impl$fireRotate(transition); + } + } + + protected final void impl$fireTeleportCrossDimensionAfter(final ServerLevel originalLevel, final ServerLevel destinationLevel, final ServerLevel originalDestinationLevel) { + Sponge.eventManager().post( + SpongeEventFactory.createChangeEntityWorldEventPost( + PhaseTracker.getWorldInstance(destinationLevel).currentCause(), + (org.spongepowered.api.entity.Entity) this, + (ServerWorld) originalLevel, + (ServerWorld) destinationLevel, + (ServerWorld) originalDestinationLevel + ) + ); + } + protected final @Nullable TeleportTransition impl$fireTeleportSameDimension(TeleportTransition transition) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) this.shadow$level()); + try (final TeleportContext context = EntityPhase.State.PORTAL_DIMENSION_CHANGE.createPhaseContext(phaseTracker)) { + context.buildAndSwitch(); + + if (ShouldFire.MOVE_ENTITY_EVENT) { + final Vector3d originalDestination = this.impl$absoluteDestinationPosition(transition); + final MoveEntityEvent moveEvent = SpongeEventFactory.createMoveEntityEvent( + phaseTracker.currentCause(), + (org.spongepowered.api.entity.Entity) this, + VecHelper.toVector3d(this.position), + originalDestination, + originalDestination + ); + transition = this.impl$fireMove(transition, moveEvent); + if (transition == null) { + return null; + } + } + + return this.impl$fireRotate(transition); + } + } + + private Vector3d impl$absoluteDestinationPosition(final TeleportTransition transition) { + final Vec3 origin = this.position; + final Vec3 pos = transition.position(); + final Set relatives = transition.relatives(); + return new Vector3d( + relatives.contains(Relative.X) ? origin.x + pos.x : pos.x, + relatives.contains(Relative.Y) ? origin.y + pos.y : pos.y, + relatives.contains(Relative.Z) ? origin.z + pos.z : pos.z + ); + } + + private @Nullable TeleportTransition impl$fireMove(final TeleportTransition transition, final MoveEntityEvent moveEvent) { + if (SpongeCommon.post(moveEvent)) { + return null; } - // TODO impl$fireMoveEvent when not changing dimensions (e.g. EndGateways) + if (moveEvent.destinationPosition().equals(moveEvent.originalDestinationPosition())) { + return transition; + } - oldLevel.getProfiler().pop(); - return newEntity; + final Set newRelatives = EnumSet.noneOf(Relative.class); + newRelatives.addAll(transition.relatives()); + newRelatives.removeAll(Constants.Entity.RELATIVE_POSITION); + + return new TeleportTransition( + transition.newLevel(), + VecHelper.toVanillaVector3d(moveEvent.destinationPosition()), + transition.deltaMovement(), + transition.yRot(), transition.xRot(), + transition.missingRespawnBlock(), + transition.asPassenger(), + newRelatives, + transition.postTeleportTransition() + ); } - @Override - public final ChangeEntityWorldEvent.Reposition bridge$fireRepositionEvent(final ServerWorld originalDestinationWorld, - final ServerWorld targetWorld, - final Vector3d destinationPosition) { + private TeleportTransition impl$fireRotate(final TeleportTransition transition) { + final Vector3d fromRot = new Vector3d(this.shadow$getXRot(), this.shadow$getYRot(), 0); - this.impl$moveEventsFired = true; - final ChangeEntityWorldEvent.Reposition reposition = SpongeEventFactory.createChangeEntityWorldEventReposition( - PhaseTracker.getInstance().currentCause(), - (org.spongepowered.api.entity.Entity) this, - (ServerWorld) this.shadow$level(), - VecHelper.toVector3d(this.position), - destinationPosition, - originalDestinationWorld, - destinationPosition, - targetWorld + final Set relatives = transition.relatives(); + final Vector3d toRot = new Vector3d( + relatives.contains(Relative.X_ROT) ? fromRot.x() + transition.xRot() : transition.xRot(), + relatives.contains(Relative.Y_ROT) ? fromRot.y() + transition.yRot() : transition.yRot(), + 0 ); - SpongeCommon.post(reposition); - return reposition; + @Nullable Vector3d newToRot = SpongeCommonEventFactory.callRotateEvent((org.spongepowered.api.entity.Entity) this, fromRot, toRot); + if (newToRot == null) { + newToRot = fromRot; // Cancelled, reset to original rotation + } + + if (newToRot.equals(toRot)) { + return transition; + } + + final Set newRelatives = EnumSet.noneOf(Relative.class); + newRelatives.addAll(transition.relatives()); + newRelatives.removeAll(Relative.ROTATION); + + return new TeleportTransition( + transition.newLevel(), + transition.position(), + transition.deltaMovement(), + (float) newToRot.y(), (float) newToRot.x(), + transition.missingRespawnBlock(), + transition.asPassenger(), + newRelatives, + transition.postTeleportTransition() + ); } @Override @@ -573,32 +627,7 @@ public Entity changeDimension(final DimensionTransition transition) { return NBTDataTypes.ENTITY; } - - protected final @org.checkerframework.checker.nullness.qual.Nullable Vector3d impl$fireMoveEvent( - final PhaseTracker phaseTracker, final Vector3d originalDestinationPosition) { - final boolean hasMovementContext = phaseTracker.currentContext().containsKey(EventContextKeys.MOVEMENT_TYPE); - if (!hasMovementContext) { - phaseTracker.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.PLUGIN); - } - - final MoveEntityEvent event = SpongeEventFactory.createMoveEntityEvent(phaseTracker.currentCause(), - (org.spongepowered.api.entity.Entity) this, VecHelper.toVector3d(this.shadow$position()), - originalDestinationPosition, - originalDestinationPosition); - - if (!hasMovementContext) { - phaseTracker.popCause(); - phaseTracker.removeContext(EventContextKeys.MOVEMENT_TYPE); - } - - if (SpongeCommon.post(event)) { - return null; - } - - return event.destinationPosition(); - } - - @Inject(method = "startRiding(Lnet/minecraft/world/entity/Entity;Z)Z", + @Inject(method = "startRiding(Lnet/minecraft/world/entity/Entity;ZZ)Z", at = @At( value = "FIELD", target = "Lnet/minecraft/world/entity/Entity;vehicle:Lnet/minecraft/world/entity/Entity;", @@ -606,9 +635,9 @@ public Entity changeDimension(final DimensionTransition transition) { ), cancellable = true ) - private void impl$onStartRiding(final Entity vehicle, final boolean force, + private void impl$onStartRiding(final Entity vehicle, final boolean force, final boolean createPortal, final CallbackInfoReturnable ci) { - if (!this.shadow$level().isClientSide && ShouldFire.RIDE_ENTITY_EVENT_MOUNT) { + if (!this.shadow$level().isClientSide() && ShouldFire.RIDE_ENTITY_EVENT_MOUNT) { PhaseTracker.getInstance().pushCause(this); if (SpongeCommon.post(SpongeEventFactory.createRideEntityEventMount(PhaseTracker.getInstance().currentCause(), (org.spongepowered.api.entity.Entity) vehicle))) { ci.cancel(); @@ -622,7 +651,7 @@ public Entity changeDimension(final DimensionTransition transition) { private void impl$onRemoveVehicle(final CallbackInfo ci) { final Entity vehicle = this.shadow$getVehicle(); if (vehicle != null) { - if (this.shadow$level().isClientSide || !ShouldFire.RIDE_ENTITY_EVENT_DISMOUNT) { + if (this.shadow$level().isClientSide() || !ShouldFire.RIDE_ENTITY_EVENT_DISMOUNT) { return; } @@ -654,23 +683,20 @@ public Entity changeDimension(final DimensionTransition transition) { @Redirect(method = "lavaHurt", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z" + target = "Lnet/minecraft/world/entity/Entity;hurtServer(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;F)Z" ) ) - private boolean impl$createLavaBlockDamageSource(final Entity entity, final DamageSource source, final float damage) { - if (this.shadow$level().isClientSide) { // Short circuit - return entity.hurt(source, damage); - } + private boolean impl$createLavaBlockDamageSource(final Entity instance, ServerLevel serverLevel, DamageSource source, float damage) { final AABB bb = this.shadow$getBoundingBox().inflate(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D); - final ServerLocation location = DamageEventUtil.findFirstMatchingBlock((Entity) (Object) this, bb, block -> + final ServerLocation location = DamageEventUtil.findFirstMatchingBlock(instance, bb, block -> block.is(Blocks.LAVA) || block.getBlock() instanceof LavaCauldronBlock); if (location != null) { var blockSource = org.spongepowered.api.event.cause.entity.damage.source.DamageSource.builder() .from((org.spongepowered.api.event.cause.entity.damage.source.DamageSource) source).block(location) .block(location.createSnapshot()).build(); - return entity.hurt((DamageSource) blockSource, damage); + return instance.hurtServer(serverLevel, (DamageSource) blockSource, damage); } - return entity.hurt(source, damage); + return instance.hurtServer(serverLevel, source, damage); } @ModifyExpressionValue(method = "move", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;collide(Lnet/minecraft/world/phys/Vec3;)Lnet/minecraft/world/phys/Vec3;")) @@ -706,9 +732,9 @@ public Entity changeDimension(final DimensionTransition transition) { @Redirect(method = "checkFallDamage", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/Block;fallOn(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/Entity;F)V")) - private void impl$onFallOnCollide(final Block block, final Level world, final BlockState state, final BlockPos pos, final Entity entity, final float fallDistance) { - if (!ShouldFire.COLLIDE_BLOCK_EVENT_FALL || world.isClientSide) { + target = "Lnet/minecraft/world/level/block/Block;fallOn(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/Entity;D)V")) + private void impl$onFallOnCollide(final Block block, final Level world, final BlockState state, final BlockPos pos, final Entity entity, final double fallDistance) { + if (!ShouldFire.COLLIDE_BLOCK_EVENT_FALL || world.isClientSide()) { block.fallOn(world, state, pos, entity, fallDistance); return; } @@ -720,14 +746,15 @@ public Entity changeDimension(final DimensionTransition transition) { } @WrapOperation( - method = "move", + method = "applyEffectsFromBlocks(Ljava/util/List;)V", at = @At( value = "INVOKE", target = "Lnet/minecraft/world/level/block/Block;stepOn(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/entity/Entity;)V" ) ) - private void impl$onStepOnCollide(final Block block, final Level world, final BlockPos pos, final BlockState state, final Entity entity, final Operation original) { - if (!ShouldFire.COLLIDE_BLOCK_EVENT_STEP_ON || world.isClientSide) { + private void impl$onStepOnCollide(final Block block, final Level world, final BlockPos pos, final BlockState state, + final Entity entity, Operation original) { + if (!ShouldFire.COLLIDE_BLOCK_EVENT_STEP_ON || world.isClientSide()) { original.call(block, world, pos, state, entity); return; } @@ -739,37 +766,33 @@ public Entity changeDimension(final DimensionTransition transition) { } } - @Redirect(method = "checkInsideBlocks", + @WrapOperation(method = "lambda$checkInsideBlocks$0", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/state/BlockState;entityInside(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/Entity;)V" + target = "Lnet/minecraft/world/level/block/state/BlockState;entityInside(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/entity/InsideBlockEffectApplier;Z)V" ) ) // doBlockCollisions - private void impl$onCheckInsideBlocksCollide(final BlockState blockState, final Level worldIn, final BlockPos pos, final Entity entityIn) { - if (!ShouldFire.COLLIDE_BLOCK_EVENT_INSIDE || worldIn.isClientSide || blockState.isAir()) { - blockState.entityInside(worldIn, pos, entityIn); + private void impl$onCheckInsideBlocksCollide( + final BlockState blockState, final Level worldIn, final BlockPos pos, final Entity entityIn, + final InsideBlockEffectApplier insideBlockEffectApplier, final boolean applies, final Operation original) { + if (!ShouldFire.COLLIDE_BLOCK_EVENT_INSIDE || worldIn.isClientSide() || blockState.isAir()) { + original.call(blockState, worldIn, pos, entityIn, insideBlockEffectApplier, applies); return; } final org.spongepowered.api.util.Direction dir = org.spongepowered.api.util.Direction.NONE; if (!SpongeCommonEventFactory.handleCollideBlockEvent(blockState.getBlock(), worldIn, pos, blockState, entityIn, dir, SpongeCommonEventFactory.CollisionType.INSIDE)) { - blockState.entityInside(worldIn, pos, entityIn); + original.call(blockState, worldIn, pos, entityIn, insideBlockEffectApplier, applies); this.impl$lastCollidedBlockPos = pos; } } - /** - * @author gabizou - January 4th, 2016 - * @reason gabizou - January 27th, 2016 - Rewrite to a redirect - *

- * This prevents sounds from being sent to the server by entities that are vanished - */ - - @Redirect(method = "playSound", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/Entity;isSilent()Z")) - private boolean impl$checkIsSilentOrInvis(final Entity entity) { - return entity.isSilent() || !this.bridge$vanishState().createsSounds(); + @Redirect(method = "isSilent", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/syncher/SynchedEntityData;get(Lnet/minecraft/network/syncher/EntityDataAccessor;)Ljava/lang/Object;" + )) + private Object impl$checkIsSilentOrInvis(final SynchedEntityData data, final EntityDataAccessor key) { + return data.get(key) || !this.bridge$vanishState().createsSounds(); } @Redirect(method = "push(Lnet/minecraft/world/entity/Entity;)V", @@ -819,18 +842,18 @@ public Entity changeDimension(final DimensionTransition transition) { */ @Inject( - method = "spawnAtLocation(Lnet/minecraft/world/item/ItemStack;F)Lnet/minecraft/world/entity/item/ItemEntity;", + method = "spawnAtLocation(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/item/ItemStack;F)Lnet/minecraft/world/entity/item/ItemEntity;", at = @At("HEAD"), cancellable = true ) public void impl$throwDropItemConstructEvent( - final ItemStack stack, final float offsetY, final CallbackInfoReturnable cir + final ServerLevel serverLevel, ItemStack stack, float offsetY, CallbackInfoReturnable cir ) { if (stack.isEmpty()) { cir.setReturnValue(null); return; } - if (((LevelBridge) this.shadow$level()).bridge$isFake()) { + if (((LevelBridge) serverLevel).bridge$isFake()) { return; } // Now the real fun begins. @@ -856,7 +879,7 @@ public Entity changeDimension(final DimensionTransition transition) { } final ItemEntity entityitem = new ItemEntity(this.shadow$level(), posX, posY, posZ, item); entityitem.setDefaultPickUpDelay(); - this.shadow$level().addFreshEntity(entityitem); + serverLevel.addFreshEntity(entityitem); cir.setReturnValue(entityitem); } } @@ -878,7 +901,8 @@ public Entity changeDimension(final DimensionTransition transition) { this.remainingFireTicks < 1 && ticks >= Constants.Entity.MINIMUM_FIRE_TICKS && this.impl$canCallIgniteEntityEvent()) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) entity.level()); + try (final CauseStackManager.StackFrame frame = phaseTracker.pushCauseFrame()) { frame.pushCause(((org.spongepowered.api.entity.Entity) this).location().world()); final IgniteEntityEvent event = SpongeEventFactory. @@ -895,7 +919,7 @@ public Entity changeDimension(final DimensionTransition transition) { .build(); final ChangeDataHolderEvent.ValueChange valueChange = SpongeEventFactory.createChangeDataHolderEventValueChange( - PhaseTracker.SERVER.currentCause(), + phaseTracker.currentCause(), transaction, (DataHolder.Mutable) this); @@ -936,21 +960,6 @@ public Entity changeDimension(final DimensionTransition transition) { return !this.impl$transient; } - - @Redirect(method = "thunderHit", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")) - private boolean impl$ThrowDamageEventWithLightingSource( - final Entity entity, final DamageSource source, final float damage, - final ServerLevel level, final LightningBolt lightningBolt - ) { - if (!this.shadow$level().isClientSide) { - return entity.hurt(source, damage); - } - var entitySource = new DamageSource(source.typeHolder(), lightningBolt); - return entity.hurt(entitySource, damage); - } - @Inject(method = "getFireImmuneTicks", at = @At(value = "HEAD"), cancellable = true) private void impl$getFireImmuneTicks(final CallbackInfoReturnable ci) { if (this.impl$hasCustomFireImmuneTicks) { @@ -959,16 +968,20 @@ public Entity changeDimension(final DimensionTransition transition) { } @Inject(method = "saveWithoutId", at = @At("RETURN")) - private void impl$WriteSpongeDataToCompound(final CompoundTag compound, final CallbackInfoReturnable ci) { - if (DataUtil.syncDataToTag(this)) { - compound.merge(this.data$getCompound()); + private void impl$WriteSpongeDataToCompound(final ValueOutput out, final CallbackInfo ci) { + if (out instanceof TagValueOutput tag) { + if (DataUtil.syncDataToTag(this)) { + // TODO - technically this shouldn't be used but we can access the build result + // here, ideally we may have to consider migrating the existing data serialization + // process to the new "style". + tag.buildResult().merge(this.data$getCompound()); + } } } @Inject(method = "load", at = @At("RETURN")) - private void impl$ReadSpongeDataFromCompound(final CompoundTag compound, final CallbackInfo ci) { - // TODO If we are in Forge data is already present - this.data$setCompound(compound); // For vanilla we set the incoming nbt + private void impl$ReadSpongeDataFromCompound(final ValueInput input, final CallbackInfo ci) { + this.data$setCompound(((TagValueInputAccessor) input).accessor$input()); // For vanilla we set the incoming nbt // Deserialize custom data... DataUtil.syncTagToData(this); this.data$setCompound(null); // done reading @@ -1008,4 +1021,10 @@ public Entity changeDimension(final DimensionTransition transition) { }*/ + @Redirect(method = "startRiding(Lnet/minecraft/world/entity/Entity;ZZ)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/EntityType;canSerialize()Z")) + private boolean impl$allowRidingAnything(final EntityType instance) { + //Vanilla has started to prevent riding non-serializable entities. + //This results in players being unable to ride other players. + return true; + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ExperienceOrbMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ExperienceOrbMixin.java index 0a56467684a..f3172f61959 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ExperienceOrbMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ExperienceOrbMixin.java @@ -24,15 +24,16 @@ */ package org.spongepowered.common.mixin.core.world.entity; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.ExperienceOrb; +import org.spongepowered.api.entity.Entity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.common.util.DamageEventUtil; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; @Mixin(ExperienceOrb.class) public abstract class ExperienceOrbMixin extends EntityMixin { @@ -45,10 +46,10 @@ public abstract class ExperienceOrbMixin extends EntityMixin { this.impl$callExpireEntityEvent(); } - @Inject(method = "hurt", cancellable = true, at = @At(value = "INVOKE", + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/ExperienceOrb;markHurt()V")) - private void attackImpl$onAttackEntityFrom(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { - if (DamageEventUtil.callOtherAttackEvent((Entity) (Object) this, source, amount).isCancelled()) { + private void attack$onHurt(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { + if (SpongeDamageTracker.callDamageEvents((Entity) this, source, damage) == null) { cir.setReturnValue(true); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/GrieferBridgeMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/GrieferBridgeMixin.java index 023be957be2..45d6df44d5c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/GrieferBridgeMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/GrieferBridgeMixin.java @@ -25,9 +25,9 @@ package org.spongepowered.common.mixin.core.world.entity; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.projectile.LargeFireball; -import net.minecraft.world.entity.projectile.SmallFireball; -import net.minecraft.world.entity.projectile.WitherSkull; +import net.minecraft.world.entity.projectile.hurtingprojectile.LargeFireball; +import net.minecraft.world.entity.projectile.hurtingprojectile.SmallFireball; +import net.minecraft.world.entity.projectile.hurtingprojectile.WitherSkull; import org.spongepowered.api.data.Keys; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.bridge.data.SpongeDataHolderBridge; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LeashableMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LeashableMixin.java index ece26fa71c3..c4ca8d3f104 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LeashableMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LeashableMixin.java @@ -28,19 +28,24 @@ import org.spongepowered.api.Sponge; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.entity.UnleashEntityEvent; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.event.tracking.PhaseTracker; +import java.util.Objects; + @Mixin(Leashable.class) public interface LeashableMixin { @Inject(method = "setLeashedTo(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/entity/Entity;Z)V", at = @At("HEAD"), cancellable = true) private static void impl$onSetLeashedTo(final E $$0, final net.minecraft.world.entity.Entity $$1, final boolean $$2, final CallbackInfo ci) { - if (!$$0.level().isClientSide) { + if (!$$0.level().isClientSide()) { final Cause currentCause = PhaseTracker.getInstance().currentCause(); if (Sponge.eventManager().post(SpongeEventFactory.createLeashEntityEvent(currentCause, (Entity) $$0))) { ci.cancel(); @@ -48,4 +53,27 @@ public interface LeashableMixin { } } + @Inject(method = "dropLeash(Lnet/minecraft/world/entity/Entity;ZZ)V", at = @At("HEAD"), cancellable = true) + private static void impl$onDropLeash(final net.minecraft.world.entity.Entity entity, final boolean $$1, final boolean $$2, final CallbackInfo ci) { + if (entity == null || entity.level().isClientSide()) { + return; + } + + Leashable.LeashData leashData = ((Leashable) (Object) entity).getLeashData(); + if (leashData == null) { + return; + } + + final net.minecraft.world.entity.Entity holder = leashData.leashHolder; + + final CauseStackManager csm = PhaseTracker.getInstance(); + csm.pushCause(Objects.requireNonNullElse(holder, entity)); + final UnleashEntityEvent event = SpongeEventFactory.createUnleashEntityEvent(csm.currentCause(), (Entity) entity); + SpongeCommon.post(event); + csm.popCause(); + if (event.isCancelled()) { + ci.cancel(); + } + } + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LightningBoltMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LightningBoltMixin.java index 75cf1bfe0c2..7a781f50997 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LightningBoltMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LightningBoltMixin.java @@ -65,7 +65,7 @@ public abstract class LightningBoltMixin extends EntityMixin { private List impl$ThrowEventAndProcess( final Level level, final Entity thisEntity, final AABB boundingBox, final Predicate predicate ) { - if (this.shadow$isRemoved() || this.shadow$level().isClientSide) { + if (this.shadow$isRemoved() || this.shadow$level().isClientSide()) { return Collections.emptyList(); } final List originalEntities = level.getEntities(thisEntity, boundingBox, predicate); @@ -87,7 +87,7 @@ public abstract class LightningBoltMixin extends EntityMixin { ) ) private void impl$ThrowEventAndProcess(final CallbackInfo ci) { - if (this.shadow$isRemoved() || this.shadow$level().isClientSide || this.life >= 0) { + if (this.shadow$isRemoved() || this.shadow$level().isClientSide() || this.life >= 0) { return; } try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin.java index 1c0f003d1e8..9777c6ef4a2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin.java @@ -26,13 +26,12 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundSource; import net.minecraft.world.InteractionHand; import net.minecraft.world.damagesource.CombatTracker; import net.minecraft.world.damagesource.DamageSource; @@ -56,8 +55,6 @@ import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.action.SleepingEvent; -import org.spongepowered.api.event.cause.entity.MovementTypes; -import org.spongepowered.api.event.entity.MoveEntityEvent; import org.spongepowered.api.event.item.inventory.UseItemStackEvent; import org.spongepowered.api.item.inventory.ItemStackSnapshot; import org.spongepowered.api.util.Ticks; @@ -70,13 +67,11 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.world.entity.LivingEntityBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.entity.living.human.HumanEntity; -import org.spongepowered.common.event.ShouldFire; import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; @@ -105,20 +100,24 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt @Shadow public abstract int shadow$getUseItemRemainingTicks(); @Shadow public abstract float shadow$getHealth(); @Shadow public abstract CombatTracker shadow$getCombatTracker(); - @Shadow public void shadow$kill() { } + @Shadow public void shadow$kill(ServerLevel level) { } @Shadow public abstract InteractionHand shadow$getUsedItemHand(); @Shadow public abstract Optional shadow$getSleepingPos(); @Shadow protected abstract void shadow$spawnItemParticles(ItemStack stack, int count); @Shadow public abstract ItemStack shadow$getItemInHand(InteractionHand hand); + @Shadow protected abstract void shadow$dropEquipment(ServerLevel level); + @Shadow protected abstract void shadow$dropAllDeathLoot(ServerLevel level, DamageSource damageSourceIn); + @Shadow @Nullable public abstract LivingEntity shadow$getKillCredit(); + @Shadow protected abstract void shadow$createWitherRose(@Nullable LivingEntity p_226298_1_); @Shadow public abstract float shadow$getMaxHealth(); @Shadow public abstract AttributeMap shadow$getAttributes(); @Shadow public abstract void shadow$clearSleepingPos(); @Shadow public abstract void shadow$setHealth(final float $$0); + @Shadow public abstract void shadow$setItemSlot(EquipmentSlot $$0, ItemStack $$1); // @formatter:on @Nullable private ItemStack impl$activeItemStackCopy; - @Nullable private Vector3d impl$preTeleportPosition; private int impl$deathEventsPosted; @@ -144,7 +143,7 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt @Inject(method = "die", at = @At("HEAD"), cancellable = true) private void impl$throwDestructEntityDeath(final DamageSource cause, final CallbackInfo ci) { - final boolean throwEvent = !((LevelBridge) this.shadow$level()).bridge$isFake() && Sponge.isServerAvailable() && Sponge.server().onMainThread(); + final boolean throwEvent = !((LevelBridge) this.shadow$level()).bridge$isFake() && Sponge.isServerAvailable() && PhaseTracker.getWorldInstance().onSidedThread(); if (!this.dead) { // isDead should be set later on in this method so we aren't re-throwing the events. if (throwEvent && this.impl$deathEventsPosted <= Constants.Sponge.MAX_DEATH_EVENTS_BEFORE_GIVING_UP) { // ignore because some moron is not resetting the entity. @@ -169,11 +168,13 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt @WrapOperation(method = "dropAllDeathLoot", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/LivingEntity;dropEquipment()V" + target = "Lnet/minecraft/world/entity/LivingEntity;dropEquipment(Lnet/minecraft/server/level/ServerLevel;)V" ) ) - protected void impl$dropInventoryWrapForPlayerOverride(final LivingEntity instance, final Operation original) { - original.call(instance); + protected void impl$dropInventoryWrapForPlayerOverride( + final LivingEntity instance, final ServerLevel level, final Operation original + ) { + original.call(instance, level); } @Inject(method = "pushEntities", at = @At("HEAD"), cancellable = true) @@ -183,52 +184,7 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt } } - @Redirect(method = "triggerItemUseEffects", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/LivingEntity;spawnItemParticles(Lnet/minecraft/world/item/ItemStack;I)V")) - private void impl$hideItemParticlesIfVanished(final LivingEntity livingEntity, final ItemStack stack, final int count) { - if (this.bridge$vanishState().createsParticles()) { - this.shadow$spawnItemParticles(stack, count); - } - } - - @Inject(method = "randomTeleport", at = @At("HEAD")) - private void impl$snapshotPositionBeforeVanillaTeleportLogic(final double x, final double y, final double z, final boolean changeState, - final CallbackInfoReturnable cir) { - this.impl$preTeleportPosition = new Vector3d(this.shadow$getX(), this.shadow$getY(), this.shadow$getZ()); - } - - @Inject(method = "randomTeleport", at = @At(value = "RETURN", ordinal = 0, shift = At.Shift.BY, by = 2), cancellable = true) - private void impl$callMoveEntityEventForTeleport(final double x, final double y, final double z, final boolean changeState, - final CallbackInfoReturnable cir) { - if (!ShouldFire.MOVE_ENTITY_EVENT) { - return; - } - - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - frame.pushCause(this); - // ENTITY_TELEPORT is our fallback context - if (!frame.currentContext().containsKey(EventContextKeys.MOVEMENT_TYPE)) { - frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.ENTITY_TELEPORT); - } - - final MoveEntityEvent event = SpongeEventFactory.createMoveEntityEvent(frame.currentCause(), - (org.spongepowered.api.entity.Entity) this, this.impl$preTeleportPosition, new Vector3d(this.shadow$getX(), this.shadow$getY(), - this.shadow$getZ()), - new Vector3d(x, y, z)); - - if (SpongeCommon.post(event)) { - this.shadow$teleportTo(this.impl$preTeleportPosition.x(), this.impl$preTeleportPosition.y(), - this.impl$preTeleportPosition.z()); - cir.setReturnValue(false); - return; - } - - this.shadow$teleportTo(event.destinationPosition().x(), event.destinationPosition().y(), - event.destinationPosition().z()); - } - } /** * @author gabizou - January 4th, 2016 @@ -241,23 +197,6 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt } } - @Redirect( - method = "eat(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/food/FoodProperties;)Lnet/minecraft/world/item/ItemStack;", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V" - ) - ) - private void impl$ignoreExperienceLevelSoundsWhileVanished(final net.minecraft.world.level.Level world, - final net.minecraft.world.entity.player.Player player, final double x, final double y, final double z, - final SoundEvent sound, final SoundSource category, final float volume, final float pitch - ) { - if (!this.bridge$vanishState().createsSounds()) { - return; - } - world.playSound(player, x, y, z, sound, category, volume, pitch); - } - @Redirect(method = "checkFallDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;sendParticles(Lnet/minecraft/core/particles/ParticleOptions;DDDIDDDD)I")) @@ -293,11 +232,12 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt @Inject(method = "startUsingItem", cancellable = true, - locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "FIELD", - target = "Lnet/minecraft/world/entity/LivingEntity;useItem:Lnet/minecraft/world/item/ItemStack;")) - private void impl$onSetActiveItemStack(final InteractionHand hand, final CallbackInfo ci, final ItemStack stack) { - if (this.shadow$level().isClientSide) { + target = "Lnet/minecraft/world/entity/LivingEntity;useItem:Lnet/minecraft/world/item/ItemStack;", + opcode = Opcodes.PUTFIELD + )) + private void impl$onSetActiveItemStack(final InteractionHand hand, final CallbackInfo ci, final @Local ItemStack stack) { + if (this.shadow$level().isClientSide()) { return; } @@ -322,7 +262,7 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/LivingEntity;useItemRemaining:I")) private void impl$getItemDuration(final LivingEntity this$0, final int count) { - if (this.shadow$level().isClientSide) { + if (this.shadow$level().isClientSide()) { this.useItemRemaining = count; } // If we're on the server, do nothing, since we already set this field on onSetActiveItemStack @@ -351,7 +291,7 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt target = "Lnet/minecraft/world/entity/LivingEntity;getUseItemRemainingTicks()I", ordinal = 0)) private int impl$onGetRemainingItemDuration(final LivingEntity self) { - if (this.shadow$level().isClientSide) { + if (this.shadow$level().isClientSide()) { return self.getUseItemRemainingTicks(); } @@ -392,9 +332,9 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt @Inject(method = "completeUsingItem", cancellable = true, at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/LivingEntity;triggerItemUseEffects(Lnet/minecraft/world/item/ItemStack;I)V")) + target = "Lnet/minecraft/world/item/ItemStack;finishUsingItem(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/LivingEntity;)Lnet/minecraft/world/item/ItemStack;")) private void impl$onUpdateItemUse(final CallbackInfo ci) { - if (this.shadow$level().isClientSide) { + if (this.shadow$level().isClientSide()) { return; } @@ -424,7 +364,7 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;setItemInHand(Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/item/ItemStack;)V")) private void impl$onSetHeldItem(final LivingEntity self, final InteractionHand hand, final ItemStack stack) { - if (this.shadow$level().isClientSide) { + if (this.shadow$level().isClientSide()) { self.setItemInHand(hand, stack); return; } @@ -469,21 +409,22 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt target = "Lnet/minecraft/world/item/ItemStack;releaseUsing(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/LivingEntity;I)V")) // stopActiveHand private void impl$onStopPlayerUsing(final ItemStack stack, final net.minecraft.world.level.Level world, final LivingEntity self, final int duration) { - if (this.shadow$level().isClientSide) { + if (this.shadow$level().isClientSide()) { stack.releaseUsing(world, self, duration); return; } - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) world); + try (final CauseStackManager.StackFrame frame = phaseTracker.pushCauseFrame()) { final ItemStackSnapshot snapshot = ItemStackUtil.snapshotOf(stack); final HandType handType = (HandType) (Object) this.shadow$getUsedItemHand(); this.impl$addSelfToFrame(frame, snapshot, handType); final Ticks ticksDuration = SpongeTicks.ticksOrInfinite(duration); - if (!SpongeCommon.post(SpongeEventFactory.createUseItemStackEventStop(PhaseTracker.getInstance().currentCause(), + if (!SpongeCommon.post(SpongeEventFactory.createUseItemStackEventStop(phaseTracker.currentCause(), ticksDuration, ticksDuration, snapshot))) { stack.releaseUsing(world, self, duration); if (self instanceof ServerPlayer) { // Log Change and capture SlotTransactions - PhaseTracker.SERVER.getPhaseContext().getTransactor().logPlayerInventoryChange(((ServerPlayer) self), PlayerInventoryTransaction.EventCreator.STANDARD); + phaseTracker.getPhaseContext().getTransactor().logPlayerInventoryChange(((ServerPlayer) self), PlayerInventoryTransaction.EventCreator.STANDARD); ((ServerPlayer) self).inventoryMenu.broadcastChanges(); } } @@ -493,7 +434,7 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt @Inject(method = "stopUsingItem", at = @At("HEAD")) private void impl$onResetActiveHand(final CallbackInfo ci) { - if (this.shadow$level().isClientSide) { + if (this.shadow$level().isClientSide()) { return; } @@ -532,7 +473,7 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt @Inject(method = "stopSleeping", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;clearSleepingPos()V")) private void impl$callFinishSleepingEvent(final CallbackInfo ci) { - if (this.shadow$level().isClientSide) { + if (this.shadow$level().isClientSide()) { return; } @@ -562,4 +503,13 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt } } + @Inject(method = "startSleeping", at = @At("HEAD"), cancellable = true) + private void impl$callPreSleepingEvent(BlockPos param0, CallbackInfo ci) { + final Cause currentCause = PhaseTracker.getInstance().currentCause(); + final BlockSnapshot snapshot = ((ServerWorld) this.shadow$level()).createSnapshot(param0.getX(), param0.getY(), param0.getZ()); + if (Sponge.eventManager().post(SpongeEventFactory.createSleepingEventPre(currentCause, snapshot, (Living) this))) { + ci.cancel(); + } + } + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java deleted file mode 100644 index ab97ddce957..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java +++ /dev/null @@ -1,399 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.entity; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.stats.Stats; -import net.minecraft.tags.DamageTypeTags; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import org.apache.logging.log4j.Level; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.*; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.bridge.world.entity.LivingEntityBridge; -import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; -import org.spongepowered.common.util.DamageEventUtil; -import org.spongepowered.common.util.PrettyPrinter; - -import java.util.ArrayList; - -@Mixin(value = LivingEntity.class, priority = 900) -public abstract class LivingEntityMixin_Attack_Impl extends EntityMixin implements LivingEntityBridge { - - //@formatter:off - @Shadow protected abstract void shadow$playHurtSound(DamageSource param0); - @Shadow protected abstract void shadow$hurtHelmet(final DamageSource $$0, final float $$1); - @Shadow protected abstract void shadow$hurtCurrentlyUsedShield(final float $$0); - @Shadow protected abstract void shadow$blockUsingShield(final LivingEntity $$0); - @Shadow protected abstract void shadow$hurtArmor(DamageSource source, float damage); - @Shadow protected abstract float shadow$getKnockback(final Entity $$0, final DamageSource $$1); - @Shadow public abstract float shadow$getAbsorptionAmount(); - @Shadow public abstract @NonNull ItemStack shadow$getWeaponItem(); - @Shadow public abstract void setAbsorptionAmount(final float $$0); - @Shadow protected int attackStrengthTicker; - @Shadow protected float lastHurt; - private float attackImpl$baseDamage; - // @formatter:on - - private float attackImpl$lastHurt; - private int attackImpl$InvulnerableTime; - - protected DamageEventUtil.Hurt attackImpl$hurt; - protected DamageEventUtil.ActuallyHurt attackImpl$actuallyHurt; - protected DamageEventUtil.DamageEventResult attackImpl$actuallyHurtResult; - protected float attackImpl$actuallyHurtFinalDamage; - protected boolean attackImpl$actuallyHurtCancelled; - protected float attackImpl$actuallyHurtBlockedDamage; - - /** - * Forge onLivingAttack Hook - */ - @Inject(method = "hurt", at = @At("HEAD"), cancellable = true) - private void attackImpl$beforeHurt(final DamageSource source, final float damageTaken, final CallbackInfoReturnable cir) { - if (source == null) { - new PrettyPrinter(60).centre().add("Null DamageSource").hr() - .addWrapped("Sponge has found a null damage source! This should NEVER happen " - + "as the DamageSource is used for all sorts of calculations. Usually" - + " this can be considered developer error. Please report the following" - + " stacktrace to the most appropriate mod/plugin available.") - .add() - .add(new IllegalArgumentException("Null DamageSource")) - .log(SpongeCommon.logger(), Level.WARN); - cir.setReturnValue(false); - } - this.attackImpl$baseDamage = damageTaken; - } - - /** - * Prepare {@link org.spongepowered.common.util.DamageEventUtil.Hurt} for damage event - */ - @Inject(method = "hurt", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/LivingEntity;noActionTime:I")) - private void attackImpl$preventEarlyBlock1(final DamageSource $$0, final float $$1, final CallbackInfoReturnable cir) { - this.attackImpl$hurt = new DamageEventUtil.Hurt($$0, new ArrayList<>()); - } - - /** - * Prevents shield usage before event - * Captures the blocked damage as a function - */ - @Redirect(method = "hurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurtCurrentlyUsedShield(F)V")) - private void attackImpl$preventEarlyBlock1(final LivingEntity instance, final float damageToShield) { - // this.hurtCurrentlyUsedShield(damageToShield); - this.attackImpl$hurt.functions().add(DamageEventUtil.createShieldFunction(instance)); - } - - /** - * Prevents shield usage before event - */ - @Redirect(method = "hurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;blockUsingShield(Lnet/minecraft/world/entity/LivingEntity;)V")) - private void attackImpl$preventEarlyBlock2(final LivingEntity instance, final LivingEntity livingDamageSource) { - // this.blockUsingShield(livingDamageSource); - } - - /** - * Capture the bonus freezing damage as a function - */ - @Inject(method = "hurt", at = @At(value = "CONSTANT", args = "floatValue=5.0F")) - private void attackImpl$freezingBonus(final DamageSource $$0, final float $$1, final CallbackInfoReturnable cir) { - this.attackImpl$hurt.functions().add(DamageEventUtil.createFreezingBonus((LivingEntity) (Object) this, $$0, 5.0F)); - } - - /** - * Prevents {@link #shadow$hurtHelmet} before the event - * Captures the hard hat damage reduction as a function - */ - @Redirect(method = "hurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurtHelmet(Lnet/minecraft/world/damagesource/DamageSource;F)V")) - private void attackImpl$hardHat(final LivingEntity instance, final DamageSource $$0, final float $$1) { - // this.hurtHelmet($$0, $$1); - this.attackImpl$hurt.functions().add(DamageEventUtil.createHardHatModifier(instance.getItemBySlot(EquipmentSlot.HEAD), 0.75F)); - } - - /** - * Capture the old values to reset if we end up cancelling or blocking. - */ - @Inject(method = "hurt", at = @At(value = "FIELD", - target = "Lnet/minecraft/world/entity/LivingEntity;walkAnimation:Lnet/minecraft/world/entity/WalkAnimationState;")) - private void attackImpl$beforeActuallyHurt(final DamageSource source, final float damageTaken, final CallbackInfoReturnable cir) { - // Save old values - this.attackImpl$lastHurt = this.lastHurt; - this.attackImpl$InvulnerableTime = this.invulnerableTime; - this.attackImpl$actuallyHurtCancelled = false; - } - - @ModifyArg(method = "hurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;actuallyHurt(Lnet/minecraft/world/damagesource/DamageSource;F)V", ordinal = 0)) - private float attackImp$useBaseDamage1(final float $$0) { - return this.attackImpl$baseDamage - this.attackImpl$lastHurt; - } - - @ModifyArg(method = "hurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;actuallyHurt(Lnet/minecraft/world/damagesource/DamageSource;F)V", ordinal = 1)) - private float attackImp$useBaseDamage2(final float $$0) { - return this.attackImpl$baseDamage; - } - - /** - * After calling #actuallyHurt (even when invulnerable), if cancelled return early or is still invulnerable - * and reset {@link #lastHurt} and {@link #invulnerableTime} - */ - @Inject(method = "hurt", locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true, - at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 0, - target = "Lnet/minecraft/world/entity/LivingEntity;actuallyHurt(Lnet/minecraft/world/damagesource/DamageSource;F)V")) - private void attackImpl$afterActuallyHurt1(final DamageSource $$0, - final float damageTaken, - final CallbackInfoReturnable cir, - final float dealtDamage, - final boolean isBlocked - ) { - if (this.attackImpl$actuallyHurtCancelled || damageTaken <= this.lastHurt) { - this.invulnerableTime = this.attackImpl$InvulnerableTime; - this.lastHurt = this.attackImpl$lastHurt; - cir.setReturnValue(false); - } - } - - /** - * After calling #actuallyHurt, if cancelled return early - * Also reset values - */ - @Inject(method = "hurt", locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true, - at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 1, - target = "Lnet/minecraft/world/entity/LivingEntity;actuallyHurt(Lnet/minecraft/world/damagesource/DamageSource;F)V")) - private void attackImpl$afterActuallyHurt2(final DamageSource $$0, - final float damageTaken, - final CallbackInfoReturnable cir, - final float dealtDamage, - final boolean isBlocked - ) { - if (this.attackImpl$actuallyHurtCancelled) { - this.invulnerableTime = this.attackImpl$InvulnerableTime; - cir.setReturnValue(false); - } - } - - - /** - * Set final damage after #actuallyHurt and lastHurt has been set. - */ - @ModifyVariable(method = "hurt", argsOnly = true, - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/DamageSource;getEntity()Lnet/minecraft/world/entity/Entity;"), - slice = @Slice( - from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;actuallyHurt(Lnet/minecraft/world/damagesource/DamageSource;F)V"), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/DamageSource;getEntity()Lnet/minecraft/world/entity/Entity;"))) - private float attackImpl$modifyDamageTaken(float damageTaken) { - return this.attackImpl$actuallyHurtFinalDamage; - } - - /** - * Sets blocked damage after #actuallyHurt - */ - @ModifyVariable(method = "hurt", ordinal = 2, - at = @At(value = "INVOKE", - target = "Lnet/minecraft/advancements/critereon/EntityHurtPlayerTrigger;trigger(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/world/damagesource/DamageSource;FFZ)V", - shift = At.Shift.AFTER)) - private float attackImpl$modifyBlockedDamage(float damageBlocked) { - return this.attackImpl$actuallyHurtBlockedDamage; - } - - @Redirect(method = "hurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;playHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)V")) - private void attackImpl$onHurtSound(final LivingEntity instance, final DamageSource $$0) { - if (this.bridge$vanishState().createsSounds()) { - this.shadow$playHurtSound($$0); - } - } - - @Redirect(method = "hurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;makeSound(Lnet/minecraft/sounds/SoundEvent;)V")) - private void attackImpl$onMakeSound(final LivingEntity instance, final SoundEvent $$0) { - if (this.bridge$vanishState().createsSounds()) { - instance.makeSound($$0); - } - } - - @Inject(method = "actuallyHurt", at = @At(value = "INVOKE",target = "Lnet/minecraft/world/entity/LivingEntity;getDamageAfterArmorAbsorb(Lnet/minecraft/world/damagesource/DamageSource;F)F")) - protected void attackImpl$startActuallyHurt(DamageSource damageSource, float originalDamage, CallbackInfo ci) { - // TODO check for direct call? - this.attackImpl$actuallyHurt = new DamageEventUtil.ActuallyHurt((LivingEntity) (Object) this, new ArrayList<>(), damageSource, originalDamage); - } - - /** - * Prevents LivingEntity#hurtArmor from running before event - * and capture the armor absorption as a function - */ - @Redirect(method = "getDamageAfterArmorAbsorb", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurtArmor(Lnet/minecraft/world/damagesource/DamageSource;F)V")) - protected void attackImpl$onDamageAfterArmorAbsorb(final LivingEntity instance, final DamageSource $$0, final float $$1) { - if (this.attackImpl$actuallyHurt != null) { - // prevents this.hurtArmor($$0, $$1); - // $$1 = CombatRules.getDamageAfterAbsorb(this, $$1, $$0, (float)this.getArmorValue(), (float)this.getAttributeValue(Attributes.ARMOR_TOUGHNESS)); - var func = DamageEventUtil.createArmorModifiers(instance, this.attackImpl$actuallyHurt.dmgSource()); - this.attackImpl$actuallyHurt.functions().add(func); - } - } - - /** - * Captures the damage resistance as a function - */ - @Inject(method = "getDamageAfterMagicAbsorb", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getEffect(Lnet/minecraft/core/Holder;)Lnet/minecraft/world/effect/MobEffectInstance;")) - protected void attackImpl$onDamageAfterMagicAbsorb(final DamageSource $$0, final float $$1, final CallbackInfoReturnable cir) { - if (this.attackImpl$actuallyHurt != null) { - var func = DamageEventUtil.createResistanceModifier(this.attackImpl$actuallyHurt.entity()); - this.attackImpl$actuallyHurt.functions().add(func); - } - } - - - /** - * Captures the damage protection as a function - */ - @WrapOperation(method = "getDamageAfterMagicAbsorb", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/CombatRules;getDamageAfterMagicAbsorb(FF)F")) - protected float attackImpl$onDetDamageProtection(float damage, float protection, Operation original) { - if (this.attackImpl$actuallyHurt != null) { - var func = DamageEventUtil.createEnchantmentModifiers(this.attackImpl$actuallyHurt.entity(), protection); - this.attackImpl$actuallyHurt.functions().add(func); - } - return original.call(damage, protection); - } - - /** - * Prevents setting absorption before event - * Captures the absorption amount as a functions - * Then calls the DamageEntityEvent - */ - @Inject(method = "setAbsorptionAmount", cancellable = true, at = @At("HEAD")) - protected void attackImpl$onSetAbsorptionAmount(final float newAmount, final CallbackInfo ci) { - if (this.attackImpl$actuallyHurt != null) { - ci.cancel(); // Always cancel this - var oldAmount = this.shadow$getAbsorptionAmount(); - if (oldAmount > 0) { - var func = DamageEventUtil.createAbsorptionModifier(this.attackImpl$actuallyHurt.entity(), oldAmount); - this.attackImpl$actuallyHurt.functions().add(func); - } - - // Use local and clear the variable to prevent re-entry if a plugin modifies the absorption hearts inside the event. - final DamageEventUtil.ActuallyHurt actuallyHurt = this.attackImpl$actuallyHurt; - this.attackImpl$actuallyHurt = null; - - this.attackImpl$actuallyHurtResult = DamageEventUtil.callLivingDamageEntityEvent(this.attackImpl$hurt, actuallyHurt); - - if (this.attackImpl$actuallyHurtResult.event().isCancelled()) { - this.attackImpl$actuallyHurtCancelled = true; - this.attackImpl$actuallyHurtFinalDamage = 0; - this.attackImpl$actuallyHurtBlockedDamage = 0; - return; // Cancel vanilla behaviour by setting absorbed & finalDamage to 0 - } - - this.attackImpl$actuallyHurtFinalDamage = (float) this.attackImpl$actuallyHurtResult.event().finalDamage(); - this.attackImpl$actuallyHurtResult.damageAbsorbed().ifPresent(absorbed -> this.setAbsorptionAmount(oldAmount - absorbed)); - this.attackImpl$actuallyHurtBlockedDamage = this.attackImpl$actuallyHurtResult.damageBlockedByShield().orElse(0f); - } - } - - /** - * Set final damage after calling {@link LivingEntity#setAbsorptionAmount} in which we called the event - * !!NOTE that var9 is actually decompiled incorrectly!! - * It is NOT the final damage value instead the method parameter is mutated - */ - @ModifyVariable(method = "actuallyHurt", ordinal = 0, - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;setAbsorptionAmount(F)V", - ordinal = 0, shift = At.Shift.AFTER), argsOnly = true) - protected float attackImpl$setFinalDamage(final float value) { - if (this.attackImpl$actuallyHurtResult.event().isCancelled()) { - return 0; - } - return this.attackImpl$actuallyHurtFinalDamage; - } - - /** - * Replay prevented - * {@link #shadow$hurtCurrentlyUsedShield} and {@link #shadow$blockUsingShield} - * {@link #shadow$hurtHelmet} - * {@link #shadow$hurtArmor} - * {@link ServerPlayer#awardStat} for {@link Stats#DAMAGE_RESISTED} and {@link Stats#DAMAGE_DEALT} - * from {@link LivingEntity#hurt} and #actuallyHurt - * - * And capture inventory changes if needed - */ - protected void attackImpl$handlePostDamage() { - final var result = this.attackImpl$actuallyHurtResult; - if (result != null && !this.attackImpl$actuallyHurtCancelled) { - final var damageSource = result.source(); - result.damageToShield().ifPresent(dmg -> { - this.shadow$hurtCurrentlyUsedShield(dmg); - if (!damageSource.is(DamageTypeTags.IS_PROJECTILE)) { - if (damageSource.getDirectEntity() instanceof LivingEntity livingSource) { - this.shadow$blockUsingShield(livingSource); - } - } - }); - result.damageToHelmet().ifPresent(dmg -> - this.shadow$hurtHelmet(damageSource, dmg)); - result.damageToArmor().ifPresent(dmg -> - this.shadow$hurtArmor(damageSource, dmg)); - result.damageResisted().ifPresent(dmg -> { - if ((Object) this instanceof ServerPlayer player) { - player.awardStat(Stats.DAMAGE_RESISTED, Math.round(dmg * 10.0F)); - } else if (damageSource.getEntity() instanceof ServerPlayer player) { - player.awardStat(Stats.DAMAGE_DEALT_RESISTED, Math.round(dmg * 10.0F)); - } - }); - - // Capture inventory change if we modified stacks - if ((result.damageToShield().isPresent() || - result.damageToHelmet().isPresent() || - result.damageToArmor().isPresent()) - && (Object) this instanceof Player player) { - PhaseTracker.SERVER.getPhaseContext().getTransactor().logPlayerInventoryChange(player, PlayerInventoryTransaction.EventCreator.STANDARD); - player.inventoryMenu.broadcastChanges(); // capture - } - } - } - - /** - * Cleanup - * also reverts {@link #attackImpl$beforeActuallyHurt} - */ - @Inject(method = "actuallyHurt", at = @At("RETURN")) - protected void attackImpl$cleanupActuallyHurt(final DamageSource $$0, final float $$1, final CallbackInfo ci) { - this.attackImpl$handlePostDamage(); - this.attackImpl$actuallyHurt = null; - this.attackImpl$actuallyHurtResult = null; - this.lastHurt = this.attackImpl$lastHurt; - } - -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Damage.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Damage.java new file mode 100644 index 00000000000..212b8b7087c --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Damage.java @@ -0,0 +1,275 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity; + +import com.llamalad7.mixinextras.expression.Expression; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Cancellable; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.core.Holder; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.objectweb.asm.Opcodes; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.bridge.world.entity.LivingEntityBridge; +import org.spongepowered.common.bridge.world.entity.TrackedDamageBridge; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageStep; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; +import org.spongepowered.common.item.util.ItemStackUtil; + +import java.util.Deque; +import java.util.LinkedList; + +@Mixin(value = LivingEntity.class, priority = 900) +public abstract class LivingEntityMixin_Damage extends EntityMixin implements LivingEntityBridge, TrackedDamageBridge { + + //@formatter:off + @Shadow protected abstract float shadow$getKnockback(final Entity entity, final DamageSource source); + @Shadow public abstract @NonNull ItemStack shadow$getWeaponItem(); + @Shadow public abstract ItemStack shadow$getItemBySlot(final EquipmentSlot slot); + @Shadow public abstract @Nullable MobEffectInstance shadow$getEffect(final Holder effect); + @Shadow public abstract double shadow$getAttributeValue(final Holder attribute); + // @formatter:on + + private final Deque damage$trackers = new LinkedList<>(); + private boolean damage$inventoryChanged = false; + + @Override + public final @Nullable SpongeDamageTracker damage$tracker() { + return this.damage$trackers.peekLast(); + } + + @Inject(method = "hurtServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;isSleeping()Z"), cancellable = true) + private void damage$firePreEvent(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { + final SpongeDamageTracker tracker = SpongeDamageTracker.callDamagePreEvent((org.spongepowered.api.entity.Entity) this, source, this.damage$getContainerDamage(damage)); + if (tracker == null) { + cir.setReturnValue(false); + } else { + this.damage$trackers.addLast(tracker); + this.damage$setContainerDamage(tracker.applyStartStep()); + } + } + + @ModifyVariable(method = "applyItemBlocking", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/component/BlocksAttacks;resolveBlockedDamage(Lnet/minecraft/world/damagesource/DamageSource;FD)F"), argsOnly = true) + private float damage$modifyBeforeShield(final float originalDamage, @Local final ItemStack stack, @Cancellable final CallbackInfoReturnable cir) { + final SpongeDamageTracker tracker = this.damage$tracker(); + if (tracker == null) { + return originalDamage; + } + final SpongeDamageStep step = tracker.newStep(DamageStepTypes.SHIELD, ItemStackUtil.snapshotOf(stack)); + final float inputDamage = (float) step.applyChildrenBefore(originalDamage); + if (step.isSkipped()) { + final float outputDamage = (float) step.applyChildrenAfter(inputDamage); + cir.setReturnValue(originalDamage - outputDamage); + } + return inputDamage; + } + + @Inject(method = "applyItemBlocking", at = @At("RETURN"), cancellable = true, slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/component/BlocksAttacks;resolveBlockedDamage(Lnet/minecraft/world/damagesource/DamageSource;FD)F") + )) + private void damage$modifyAfterShield(final ServerLevel level, final DamageSource source, float damage, final CallbackInfoReturnable cir) { + final SpongeDamageTracker tracker = this.damage$tracker(); + if (tracker == null) { + return; + } + final SpongeDamageStep step = tracker.currentStep(DamageStepTypes.SHIELD); + if (step == null) { + return; + } + damage = (float) step.applyChildrenAfter(damage - cir.getReturnValueF()); + cir.setReturnValue((float) step.damageBeforeChildren().getAsDouble() - damage); + } + + @SuppressWarnings("MixinAnnotationTarget") + @Inject(method = "applyItemBlocking", at = { + @At(value = "INVOKE", target = "Lnet/minecraft/world/item/component/BlocksAttacks;hurtBlockingItem(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/InteractionHand;F)V"), // Vanilla and Forge + @At(value = "INVOKE", target = "Lnet/minecraft/world/item/component/BlocksAttacks;hurtBlockingItem(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/InteractionHand;FI)V") // Neo + }) + private void damage$onHurtShield(final CallbackInfoReturnable cir) { + this.damage$inventoryChanged = true; + } + + // The expression is "vague" due to a Forge bug in 26.1 that it strips the LVT entirely + // leaving us with little options to target the local variable assignment + @Expression("? > 0.0") + @ModifyExpressionValue(method = "hurtServer", at = @At("MIXINEXTRAS:EXPRESSION"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;applyItemBlocking(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;F)F"), + to = @At(value = "FIELD", target = "Lnet/minecraft/tags/DamageTypeTags;IS_FREEZING:Lnet/minecraft/tags/TagKey;", opcode = Opcodes.GETSTATIC)) + ) + private boolean damage$setBlockedFlag(final boolean blocked) { + final SpongeDamageTracker tracker = this.damage$tracker(); + if (tracker == null) { + return blocked; + } + final SpongeDamageStep step = tracker.lastStep(DamageStepTypes.SHIELD); + return step == null ? blocked : step.damageAfterSelf().getAsDouble() < step.damageBeforeSelf().getAsDouble(); + } + + @ModifyVariable(method = "hurtServer", at = @At("LOAD"), argsOnly = true, slice = @Slice( + from = @At(value = "FIELD", target = "Lnet/minecraft/tags/EntityTypeTags;FREEZE_HURTS_EXTRA_TYPES:Lnet/minecraft/tags/TagKey;"), + to = @At(value = "FIELD", target = "Lnet/minecraft/tags/DamageTypeTags;DAMAGES_HELMET:Lnet/minecraft/tags/TagKey;") + )) + private float damage$modifyBeforeFreezingBonus(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.startStep(DamageStepTypes.FREEZING_BONUS, damage, tracker.preEvent().source(), this); + } + + @ModifyVariable(method = "hurtServer", at = @At("STORE"), argsOnly = true, slice = @Slice( + from = @At(value = "FIELD", target = "Lnet/minecraft/tags/EntityTypeTags;FREEZE_HURTS_EXTRA_TYPES:Lnet/minecraft/tags/TagKey;"), + to = @At(value = "FIELD", target = "Lnet/minecraft/tags/DamageTypeTags;DAMAGES_HELMET:Lnet/minecraft/tags/TagKey;") + )) + private float damage$modifyAfterFreezingBonus(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.endStep(DamageStepTypes.FREEZING_BONUS, damage); + } + + @ModifyVariable(method = "hurtServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurtHelmet(Lnet/minecraft/world/damagesource/DamageSource;F)V"), argsOnly = true) + private float damage$modifyBeforeHardHat(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.startStep(DamageStepTypes.HARD_HAT, damage, this.shadow$getItemBySlot(EquipmentSlot.HEAD)); + } + + @WrapWithCondition(method = "hurtServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurtHelmet(Lnet/minecraft/world/damagesource/DamageSource;F)V")) + private boolean damage$skipHardHat(final LivingEntity self, final DamageSource source, final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + if (tracker == null || !tracker.isSkipped(DamageStepTypes.HARD_HAT)) { + this.damage$inventoryChanged = true; + return true; + } + return false; + } + + @ModifyVariable(method = "hurtServer", at = @At("STORE"), argsOnly = true, slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurtHelmet(Lnet/minecraft/world/damagesource/DamageSource;F)V"), + to = @At(value = "INVOKE", target = "Ljava/lang/Float;isNaN(F)Z") + )) + private float damage$modifyAfterHardHat(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.endStep(DamageStepTypes.HARD_HAT, damage); + } + + @ModifyVariable(method = "getDamageAfterArmorAbsorb", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurtArmor(Lnet/minecraft/world/damagesource/DamageSource;F)V"), argsOnly = true) + private float damage$modifyBeforeArmor(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.startStep(DamageStepTypes.ARMOR, damage, this, Attributes.ARMOR_TOUGHNESS); + } + + @WrapWithCondition(method = "getDamageAfterArmorAbsorb", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurtArmor(Lnet/minecraft/world/damagesource/DamageSource;F)V")) + private boolean damage$skipArmor(final LivingEntity self, final DamageSource source, final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + if (tracker == null || !tracker.isSkipped(DamageStepTypes.ARMOR)) { + this.damage$inventoryChanged = true; + return true; + } + return false; + } + + @ModifyVariable(method = "getDamageAfterArmorAbsorb", at = @At("STORE"), argsOnly = true) + private float damage$modifyAfterArmor(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.endStep(DamageStepTypes.ARMOR, damage); + } + + @ModifyVariable(method = "getDamageAfterMagicAbsorb", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getEffect(Lnet/minecraft/core/Holder;)Lnet/minecraft/world/effect/MobEffectInstance;"), argsOnly = true) + private float damage$modifyBeforeDefensivePotionEffect(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.startStep(DamageStepTypes.DEFENSIVE_POTION_EFFECT, damage, this.shadow$getEffect(MobEffects.RESISTANCE)); + } + + @ModifyVariable(method = "getDamageAfterMagicAbsorb", at = @At("STORE"), argsOnly = true, slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getEffect(Lnet/minecraft/core/Holder;)Lnet/minecraft/world/effect/MobEffectInstance;"), + to = @At(value = "FIELD", target = "Lnet/minecraft/stats/Stats;DAMAGE_RESISTED:Lnet/minecraft/resources/Identifier;"))) + private float damage$modifyAfterDefensivePotionEffect(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.endStep(DamageStepTypes.DEFENSIVE_POTION_EFFECT, damage); + } + + @WrapOperation(method = "getDamageAfterMagicAbsorb", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/CombatRules;getDamageAfterMagicAbsorb(FF)F")) + private float damage$modifyBeforeAndAfterArmorEnchantment(float damage, final float protection, final Operation operation) { + final SpongeDamageTracker tracker = this.damage$tracker(); + if (tracker == null) { + return operation.call(damage, protection); + } + + final SpongeDamageStep step = tracker.newStep(DamageStepTypes.ARMOR_ENCHANTMENT, this); + damage = (float) step.applyChildrenBefore(damage); + if (!step.isSkipped()) { + damage = operation.call(damage, protection); + } + return (float) step.applyChildrenAfter(damage); + } + + @WrapWithCondition(method = "hurtServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;playHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)V")) + private boolean damage$onHurtSound(final LivingEntity self, final DamageSource source) { + return this.bridge$vanishState().createsSounds(); + } + + @WrapWithCondition(method = "hurtServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;makeSound(Lnet/minecraft/sounds/SoundEvent;)V")) + private boolean damage$onMakeSound(final LivingEntity self, final SoundEvent sound) { + return this.bridge$vanishState().createsSounds(); + } + + @Inject(method = "hurtServer", at = @At("RETURN"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;isSleeping()Z", shift = At.Shift.AFTER) + )) + private void damage$removeTrackerAndCaptureInventory(final CallbackInfoReturnable cir) { + this.damage$trackers.removeLast(); + + if (this.damage$inventoryChanged) { + this.damage$inventoryChanged = false; + + if ((Object) this instanceof Player player) { + PhaseTracker.getWorldInstance((ServerLevel) player.level()).getPhaseContext().getTransactor().logPlayerInventoryChange(player, PlayerInventoryTransaction.EventCreator.STANDARD); + player.inventoryMenu.broadcastChanges(); + } + } + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Shared_Damage.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Shared_Damage.java new file mode 100644 index 00000000000..0b4c0cde3e3 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Shared_Damage.java @@ -0,0 +1,73 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import net.minecraft.resources.Identifier; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.LivingEntity; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.common.bridge.world.entity.TrackedDamageBridge; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageStep; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; + +// Forge and Vanilla +@Mixin(value = LivingEntity.class, priority = 900) +public abstract class LivingEntityMixin_Shared_Damage implements TrackedDamageBridge { + + @ModifyVariable(method = "hurtServer", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/LivingEntity;noActionTime:I"), argsOnly = true) + private float damage$modifyAfterStart(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + if (tracker == null) { + return damage; + } + final SpongeDamageStep step = tracker.currentStep(DamageStepTypes.START); + return step == null ? damage : (float) step.damageAfterChildren().getAsDouble(); + } + + @ModifyVariable(method = "actuallyHurt", at = @At("STORE"), argsOnly = true, slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getDamageAfterMagicAbsorb(Lnet/minecraft/world/damagesource/DamageSource;F)F"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getAbsorptionAmount()F", ordinal = 0))) + private float damage$modifyBeforeAbsorption(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.startStep(DamageStepTypes.ABSORPTION, damage, this); + } + + @WrapWithCondition(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;setAbsorptionAmount(F)V", ordinal = 0)) + private boolean damage$skipAbsorption(final LivingEntity self, final float absorption) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null || !tracker.isSkipped(DamageStepTypes.ABSORPTION); + } + + @WrapWithCondition(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;awardStat(Lnet/minecraft/resources/Identifier;I)V")) + private boolean damage$skipAbsorptionStat(final ServerPlayer self, final Identifier stat, final int amount) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null || !tracker.isSkipped(DamageStepTypes.ABSORPTION); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/MobMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/MobMixin.java index 49aeaefa78e..2b8db878368 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/MobMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/MobMixin.java @@ -24,7 +24,6 @@ */ package org.spongepowered.common.mixin.core.world.entity; -import net.minecraft.world.entity.Leashable; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.goal.GoalSelector; @@ -34,10 +33,6 @@ import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.ai.goal.GoalExecutorTypes; import org.spongepowered.api.entity.living.Agent; -import org.spongepowered.api.entity.living.Living; -import org.spongepowered.api.event.CauseStackManager; -import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.entity.UnleashEntityEvent; import org.spongepowered.api.event.entity.ai.SetAITargetEvent; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -47,14 +42,12 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.world.entity.GrieferBridge; import org.spongepowered.common.bridge.world.entity.ai.goal.GoalSelectorBridge; import org.spongepowered.common.bridge.world.entity.player.PlayerBridge; import org.spongepowered.common.entity.EntityUtil; import org.spongepowered.common.event.ShouldFire; import org.spongepowered.common.event.SpongeCommonEventFactory; -import org.spongepowered.common.event.tracking.PhaseTracker; @Mixin(Mob.class) public abstract class MobMixin extends LivingEntityMixin { @@ -63,11 +56,10 @@ public abstract class MobMixin extends LivingEntityMixin { @Shadow @Final protected GoalSelector goalSelector; @Shadow @Final protected GoalSelector targetSelector; @Shadow @Nullable private LivingEntity target; - @Shadow public abstract Leashable.LeashData shadow$getLeashData(); @Shadow protected abstract void shadow$registerGoals(); + @Shadow public abstract boolean shadow$isSaddled(); // @formatter:on - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Mob;registerGoals()V")) private void impl$registerGoals(final Mob this$0) { this.impl$setupGoalSelectors(); @@ -87,34 +79,6 @@ public abstract class MobMixin extends LivingEntityMixin { } } - @Inject(method = "dropLeash", cancellable = true, - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Leashable;dropLeash(ZZ)V")) - private void impl$ThrowUnleashEvent(final boolean sendPacket, final boolean dropLead, final CallbackInfo ci) { - if (this.shadow$level().isClientSide) { - return; - } - - var leashData = this.shadow$getLeashData(); - if (leashData == null) { - return; - } - - final net.minecraft.world.entity.Entity entity = leashData.leashHolder; - - final CauseStackManager csm = PhaseTracker.getInstance(); - if (entity == null) { - csm.pushCause(this); - } else { - csm.pushCause(entity); - } - final UnleashEntityEvent event = SpongeEventFactory.createUnleashEntityEvent(csm.currentCause(), (Living) this); - SpongeCommon.post(event); - csm.popCause(); - if (event.isCancelled()) { - ci.cancel(); - } - } - /** * @author gabizou - January 4th, 2016 * @@ -125,7 +89,7 @@ public abstract class MobMixin extends LivingEntityMixin { */ @Inject(method = "setTarget", at = @At("HEAD"), cancellable = true) private void onSetAttackTarget(@Nullable final LivingEntity entitylivingbaseIn, final CallbackInfo ci) { - if (this.shadow$level().isClientSide || entitylivingbaseIn == null) { + if (this.shadow$level().isClientSide() || entitylivingbaseIn == null) { return; } //noinspection ConstantConditions diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/MobMixin_Attack.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/MobMixin_Attack.java new file mode 100644 index 00000000000..c77f998bfb7 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/MobMixin_Attack.java @@ -0,0 +1,115 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Cancellable; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.bridge.world.entity.TrackedAttackBridge; +import org.spongepowered.common.event.cause.entity.damage.SpongeAttackTracker; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageStep; + +import java.util.Deque; +import java.util.LinkedList; + +@Mixin(Mob.class) +public abstract class MobMixin_Attack extends LivingEntityMixin_Damage implements TrackedAttackBridge { + private final Deque attack$trackers = new LinkedList<>(); + + @Override + public final @Nullable SpongeAttackTracker attack$tracker() { + return this.attack$trackers.peekLast(); + } + + @WrapOperation( + method = "doHurtTarget", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/enchantment/EnchantmentHelper;modifyDamage(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;F)F") + ) + private float attack$firePreEvent(final ServerLevel level, final ItemStack weapon, final Entity target, final DamageSource source, float damage, final Operation original, @Cancellable CallbackInfoReturnable cir) { + final SpongeAttackTracker tracker = SpongeAttackTracker.callAttackPreEvent((org.spongepowered.api.entity.Entity) target, source, damage, weapon); + if (tracker == null) { + cir.setReturnValue(false); + return damage; + } + + this.attack$trackers.addLast(tracker); + damage = tracker.applyStartStep(); + + return original.call(level, weapon, target, source, damage); + } + + @WrapOperation(method = "doHurtTarget", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/Item;getAttackDamageBonus(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) + private float attack$modifyBeforeAndAfterWeaponBonus(final Item item, final Entity target, final float originalDamage, final DamageSource source, final Operation operation) { + final SpongeAttackTracker tracker = this.attack$tracker(); + if (tracker == null) { + return operation.call(item, target, originalDamage, source); + } + + final SpongeDamageStep step = tracker.newStep(DamageStepTypes.WEAPON_BONUS, tracker.weaponSnapshot()); + float damage = (float) step.applyChildrenBefore(originalDamage); + if (!step.isSkipped()) { + damage += operation.call(item, target, damage, source); + } + return (float) step.applyChildrenAfter(damage) - originalDamage; + } + + @WrapOperation(method = "doHurtTarget", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurtServer(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;F)Z")) + private boolean attack$firePostEvent(final Entity target, final ServerLevel level, final DamageSource source, float damage, final Operation operation) { + final SpongeAttackTracker tracker = this.attack$tracker(); + if (tracker != null) { + final float knockbackModifier = this.shadow$getKnockback(target, source); + if (tracker.callAttackPostEvent((org.spongepowered.api.entity.Entity) target, source, damage, knockbackModifier)) { + return false; + } + damage = (float) tracker.postEvent().finalDamage(); + } + return operation.call(target, level, source, damage); + } + + @WrapOperation(method = "doHurtTarget", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Mob;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")) + private float attack$knockbackModifier(final Mob self, final Entity target, final DamageSource source, final Operation operation) { + final SpongeAttackTracker tracker = this.attack$tracker(); + return tracker == null ? operation.call(self, target, source) : (float) tracker.postEvent().knockbackModifier(); + } + + @Inject(method = "doHurtTarget", at = @At("RETURN"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/enchantment/EnchantmentHelper;modifyDamage(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;F)F", shift = At.Shift.AFTER))) + private void attack$removeTracker(CallbackInfoReturnable cir) { + this.attack$trackers.removeLast(); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/MobMixin_Attack_Impl.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/MobMixin_Attack_Impl.java deleted file mode 100644 index 4142c751fad..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/MobMixin_Attack_Impl.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.entity; - -import net.minecraft.core.Holder; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.ai.attributes.Attribute; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.util.DamageEventUtil; - -import java.util.ArrayList; - -@Mixin(Mob.class) -public abstract class MobMixin_Attack_Impl extends LivingEntityMixin_Attack_Impl { - - private double impl$hurtTargetDamage; - private double impl$knockbackModifier; - - @Redirect(method = "doHurtTarget", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/Mob;getAttributeValue(Lnet/minecraft/core/Holder;)D")) - private double attackImpl$onCanGrief(final Mob instance, final Holder attackDamageAttribute) { - this.impl$hurtTargetDamage = instance.getAttributeValue(attackDamageAttribute); - return this.impl$hurtTargetDamage; - } - - @Redirect(method = "doHurtTarget", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")) - private boolean attackImpl$onCanGrief(final net.minecraft.world.entity.Entity targetEntity, final DamageSource damageSource, final float mcFinalDamage) { - final var thisEntity = (Mob) (Object) this; - - float knockbackModifier = this.shadow$getKnockback(targetEntity, damageSource); - - var attack = new DamageEventUtil.Attack<>(thisEntity, targetEntity, this.shadow$getWeaponItem(), damageSource, 1, (float) this.impl$hurtTargetDamage, new ArrayList<>()); - if (this.shadow$level() instanceof ServerLevel) { - // baseDamage = EnchantmentHelper.modifyDamage(level, thisEntity.getWeaponItem(), targetEntity, damageSource, baseDamage);// - attack.functions().addAll(DamageEventUtil.createAttackEnchantmentFunction(attack.weapon(), targetEntity, damageSource)); - } - - final var event = DamageEventUtil.callMobAttackEvent(attack, knockbackModifier); - this.impl$knockbackModifier = event.knockbackModifier(); - - return targetEntity.hurt(damageSource, (float) event.finalOutputDamage()); - } - - @Redirect(method = "doHurtTarget", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/Mob;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")) - private float attackImpl$onCanGrief(final Mob instance, final net.minecraft.world.entity.Entity entity, final DamageSource damageSource) { - return (float) this.impl$knockbackModifier; - } - -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/PortalProcessorMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/PortalProcessorMixin.java index b37327794a0..49ced1bac1f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/PortalProcessorMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/PortalProcessorMixin.java @@ -26,13 +26,13 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.PortalProcessor; +import net.minecraft.world.entity.Relative; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Portal; -import net.minecraft.world.level.portal.DimensionTransition; +import net.minecraft.world.level.portal.TeleportTransition; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.SpongeEventFactory; @@ -48,10 +48,11 @@ import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.event.tracking.phase.entity.EntityPhase; import org.spongepowered.common.event.tracking.phase.entity.TeleportContext; -import org.spongepowered.common.hooks.PlatformHooks; import org.spongepowered.common.util.VecHelper; import org.spongepowered.math.vector.Vector3d; +import java.util.EnumSet; + @Mixin(PortalProcessor.class) public abstract class PortalProcessorMixin implements PortalProcessorBridge { // @formatter:off @@ -73,18 +74,14 @@ public abstract class PortalProcessorMixin implements PortalProcessorBridge { @Redirect(method = "getPortalDestination", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/Portal;getPortalDestination(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/portal/DimensionTransition;")) - public DimensionTransition impl$onGetPortalDestination(final Portal instance, final ServerLevel serverLevel, final Entity entity, final BlockPos blockPos) { - final var spongEntity = (org.spongepowered.api.entity.Entity) entity; - + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/Portal;getPortalDestination(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/portal/TeleportTransition;")) + public TeleportTransition impl$onGetPortalDestination(final Portal instance, final ServerLevel serverLevel, final Entity entity, final BlockPos blockPos) { + final var spongeEntity = (org.spongepowered.api.entity.Entity) entity; var finalPortal = instance; - final var contextToSwitchTo = EntityPhase.State.PORTAL_DIMENSION_CHANGE.createPhaseContext(PhaseTracker.getInstance()).worldChange(); - if (entity instanceof ServerPlayer) { - contextToSwitchTo.player(); - } - try (final TeleportContext context = contextToSwitchTo.buildAndSwitch(); - final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(serverLevel); + try (final TeleportContext context = EntityPhase.State.PORTAL_DIMENSION_CHANGE.createPhaseContext(phaseTracker).worldChange().buildAndSwitch(); + final CauseStackManager.StackFrame frame = phaseTracker.pushCauseFrame()) { frame.pushCause(entity); var be = serverLevel.getBlockEntity(this.entryPosition); if (be != null) { @@ -98,7 +95,7 @@ public abstract class PortalProcessorMixin implements PortalProcessorBridge { frame.addContext(EventContextKeys.MOVEMENT_TYPE, movementType); frame.addContext(EventContextKeys.PORTAL_LOGIC, (PortalLogic) portal); - var preEvent = SpongeEventFactory.createInvokePortalEventPrepare(frame.currentCause(), spongEntity, (PortalLogic) this.portal); + var preEvent = SpongeEventFactory.createInvokePortalEventPrepare(frame.currentCause(), spongeEntity, (PortalLogic) this.portal); if (SpongeCommon.post(preEvent)) { return null; } @@ -115,23 +112,28 @@ public abstract class PortalProcessorMixin implements PortalProcessorBridge { frame.pushCause(finalPortal); frame.addContext(EventContextKeys.PORTAL, (org.spongepowered.api.world.portal.Portal) this); - var preWorldChangeEvent = PlatformHooks.INSTANCE.getEventHooks().callChangeEntityWorldEventPre(entity, transition.newLevel()); + final var preWorldChangeEvent = SpongeEventFactory.createChangeEntityWorldEventPre( + frame.currentCause(), + (org.spongepowered.api.entity.Entity) entity, + (ServerWorld) entity.level(), + (ServerWorld) transition.newLevel(), + (ServerWorld) transition.newLevel()); if (preWorldChangeEvent.isCancelled()) { wrapperTransaction.markCancelled(); return null; } var tpEvent = SpongeEventFactory.createInvokePortalEventExecute(frame.currentCause(), - spongEntity, + spongeEntity, (ServerWorld) entity.level(), new Vector3d(transition.xRot(), transition.yRot(), 0), - spongEntity.position(), - spongEntity.rotation(), + spongeEntity.position(), + spongeEntity.rotation(), (ServerWorld) transition.newLevel(), - VecHelper.toVector3d(transition.pos()), - VecHelper.toVector3d(transition.pos()), + VecHelper.toVector3d(transition.position()), + VecHelper.toVector3d(transition.position()), preWorldChangeEvent.destinationWorld(), // with modified preWorldChangeEvent destination world - VecHelper.toVector3d(transition.speed()), + VecHelper.toVector3d(transition.deltaMovement()), (PortalLogic) finalPortal); if (SpongeCommon.post(tpEvent)) { wrapperTransaction.markCancelled(); @@ -139,13 +141,15 @@ public abstract class PortalProcessorMixin implements PortalProcessorBridge { } // modify transition after event - return new DimensionTransition((ServerLevel) tpEvent.destinationWorld(), + return new TeleportTransition((ServerLevel) tpEvent.destinationWorld(), VecHelper.toVanillaVector3d(tpEvent.destinationPosition()), VecHelper.toVanillaVector3d(tpEvent.exitSpeed()), (float) tpEvent.toRotation().y(), (float) tpEvent.toRotation().x(), transition.missingRespawnBlock(), - transition.postDimensionTransition()); + transition.asPassenger(), + EnumSet.noneOf(Relative.class), + transition.postTeleportTransition()); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/behavior/HarvestFarmlandMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/behavior/HarvestFarmlandMixin.java index 140f7542326..18883133a52 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/behavior/HarvestFarmlandMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/behavior/HarvestFarmlandMixin.java @@ -24,9 +24,9 @@ */ package org.spongepowered.common.mixin.core.world.entity.ai.behavior; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.ai.behavior.HarvestFarmland; -import net.minecraft.world.entity.npc.Villager; -import org.spongepowered.api.world.server.ServerWorld; +import net.minecraft.world.entity.npc.villager.Villager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -42,8 +42,8 @@ public abstract class HarvestFarmlandMixin { * would fail in forge environments. This changes the injection to a predictable * place where we still can forcibly call things but still cancel as needed. */ - @Inject(method = "canStillUse", at = @At(value = "HEAD"), cancellable = true) - private void onCanGrief(final CallbackInfoReturnable cir, ServerWorld worldIn, Villager owner) { + @Inject(method = "canStillUse(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/npc/villager/Villager;J)Z", at = @At(value = "HEAD"), cancellable = true) + private void onCanGrief(ServerLevel level, Villager owner, long $$2, CallbackInfoReturnable cir) { if (!((GrieferBridge) owner).bridge$canGrief()) { cir.setReturnValue(false); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/goal/GoalMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/goal/GoalMixin.java index ab09ccf8e03..48052fae4f1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/goal/GoalMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/goal/GoalMixin.java @@ -29,6 +29,7 @@ import org.spongepowered.api.entity.ai.goal.GoalExecutor; import org.spongepowered.api.entity.ai.goal.GoalType; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -42,6 +43,8 @@ @Mixin(Goal.class) public abstract class GoalMixin implements GoalBridge { + @Shadow protected abstract int shadow$adjustedTickDelay(int $$0); + private Supplier impl$type; private GoalExecutor impl$owner; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/goal/RunAroundLikeCrazyGoalMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/goal/RunAroundLikeCrazyGoalMixin.java index 46eab5028f0..beef75b6dc5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/goal/RunAroundLikeCrazyGoalMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/goal/RunAroundLikeCrazyGoalMixin.java @@ -25,7 +25,7 @@ package org.spongepowered.common.mixin.core.world.entity.ai.goal; import net.minecraft.world.entity.ai.goal.RunAroundLikeCrazyGoal; -import net.minecraft.world.entity.animal.horse.AbstractHorse; +import net.minecraft.world.entity.animal.equine.AbstractHorse; import org.spongepowered.api.entity.living.animal.horse.HorseLike; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.SpongeEventFactory; @@ -51,7 +51,7 @@ public abstract class RunAroundLikeCrazyGoalMixin extends GoalMixin { method = "tick", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/entity/animal/horse/AbstractHorse;tameWithName(Lnet/minecraft/world/entity/player/Player;)Z" + target = "Lnet/minecraft/world/entity/animal/equine/AbstractHorse;tameWithName(Lnet/minecraft/world/entity/player/Player;)Z" ), cancellable = true ) @@ -68,7 +68,7 @@ public abstract class RunAroundLikeCrazyGoalMixin extends GoalMixin { method = "tick", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/entity/animal/horse/AbstractHorse;ejectPassengers()V" + target = "Lnet/minecraft/world/entity/animal/equine/AbstractHorse;ejectPassengers()V" ), cancellable = true ) diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/sensing/SensorMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/sensing/SensorMixin.java index dbcf9ecd5af..8c032e63cea 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/sensing/SensorMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/sensing/SensorMixin.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.mixin.core.world.entity.ai.sensing; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.sensing.Sensor; import org.spongepowered.asm.mixin.Mixin; @@ -40,12 +41,12 @@ public class SensorMixin { "isEntityAttackable", "isEntityAttackableIgnoringLineOfSight" }, at = @At("HEAD"), cancellable = true) - private static void impl$cancelForVanishedEntities(LivingEntity $$0, LivingEntity $$1, CallbackInfoReturnable cir) { - final var vs = ((VanishableBridge) $$0).bridge$vanishState(); + private static void impl$cancelForVanishedEntities(ServerLevel $$0, LivingEntity $$1, LivingEntity $$2, CallbackInfoReturnable cir) { + final var vs = ((VanishableBridge) $$1).bridge$vanishState(); if (vs.invisible() && vs.untargetable()) { cir.setReturnValue(false); } - final var vsOther = ((VanishableBridge) $$1).bridge$vanishState(); + final var vsOther = ((VanishableBridge) $$2).bridge$vanishState(); if (vsOther.invisible() && vsOther.untargetable()) { cir.setReturnValue(false); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/horse/AbstractHorseMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/equine/AbstractHorseMixin.java similarity index 85% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/horse/AbstractHorseMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/equine/AbstractHorseMixin.java index a95b5af8bb9..95ee43bfccc 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/horse/AbstractHorseMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/equine/AbstractHorseMixin.java @@ -22,14 +22,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.animal.horse; +package org.spongepowered.common.mixin.core.world.entity.animal.equine; -import net.minecraft.sounds.SoundSource; import net.minecraft.world.SimpleContainer; -import net.minecraft.world.entity.animal.horse.AbstractHorse; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.animal.equine.AbstractHorse; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.bridge.world.entity.animal.horse.AbstractHorseBridge; @@ -40,8 +39,6 @@ public abstract class AbstractHorseMixin extends AgableMobMixin implements Abstr // @formatter:off @Shadow protected SimpleContainer inventory; - @Shadow public abstract void shadow$equipSaddle(ItemStack stack, @Nullable SoundSource sound); - @Shadow public abstract boolean shadow$isSaddled(); // @formatter:on @Override @@ -52,7 +49,7 @@ public abstract class AbstractHorseMixin extends AgableMobMixin implements Abstr @Override public void bridge$setSaddled(boolean saddled) { if (!this.shadow$isSaddled() && saddled) { - this.shadow$equipSaddle(new ItemStack(Items.SADDLE), null); + this.shadow$setItemSlot(EquipmentSlot.SADDLE, new ItemStack(Items.SADDLE)); } else if (this.shadow$isSaddled() && !saddled){ this.inventory.setItem(0, ItemStack.EMPTY); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/Cat_CatRelaxOnOwnerGoalMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/feline/Cat_CatRelaxOnOwnerGoalMixin.java similarity index 86% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/Cat_CatRelaxOnOwnerGoalMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/feline/Cat_CatRelaxOnOwnerGoalMixin.java index f4181d3b388..8d45ca231e6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/Cat_CatRelaxOnOwnerGoalMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/feline/Cat_CatRelaxOnOwnerGoalMixin.java @@ -22,22 +22,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.animal; +package org.spongepowered.common.mixin.core.world.entity.animal.feline; -import net.minecraft.world.entity.animal.Cat; +import net.minecraft.world.entity.animal.feline.Cat; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(targets = "net/minecraft/world/entity/animal/Cat$CatRelaxOnOwnerGoal") +@Mixin(targets = "net/minecraft/world/entity/animal/feline/Cat$CatRelaxOnOwnerGoal") public abstract class Cat_CatRelaxOnOwnerGoalMixin { private boolean impl$teleportResult; @Redirect(method = "giveMorningGift()V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Cat;randomTeleport(DDDZ)Z")) + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/feline/Cat;randomTeleport(DDDZ)Z")) private boolean impl$storeTeleportResult(Cat entity, double x, double y, double z, boolean changeState) { this.impl$teleportResult = entity.randomTeleport(x, y, z, changeState); return this.impl$teleportResult; @@ -45,7 +45,7 @@ public abstract class Cat_CatRelaxOnOwnerGoalMixin { @Inject(method = "giveMorningGift()V", at = @At(value = "INVOKE_ASSIGN", - target = "Lnet/minecraft/world/entity/animal/Cat;randomTeleport(DDDZ)Z", + target = "Lnet/minecraft/world/entity/animal/feline/Cat;randomTeleport(DDDZ)Z", shift = At.Shift.AFTER), cancellable = true) private void impl$makeCatsRespectTeleportResult(final CallbackInfo ci) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/OcelotMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/feline/OcelotMixin.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/OcelotMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/feline/OcelotMixin.java index 6ac10940bc5..ed1788b7a36 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/OcelotMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/feline/OcelotMixin.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.animal; +package org.spongepowered.common.mixin.core.world.entity.animal.feline; import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionHand; @@ -38,7 +38,7 @@ import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.mixin.core.world.entity.AgableMobMixin; -@Mixin(net.minecraft.world.entity.animal.Ocelot.class) +@Mixin(net.minecraft.world.entity.animal.feline.Ocelot.class) public abstract class OcelotMixin extends AgableMobMixin { @Redirect(method = "mobInteract", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/RandomSource;nextInt(I)I")) diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/fish/PufferfishMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/fish/PufferfishMixin.java new file mode 100644 index 00000000000..5bea2688bf0 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/fish/PufferfishMixin.java @@ -0,0 +1,66 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity.animal.fish; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.animal.fish.Pufferfish; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.bridge.data.VanishableBridge; + +@Mixin(Pufferfish.class) +public class PufferfishMixin { + + @WrapOperation(method = "lambda$static$0", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;isCreative()Z")) + private static boolean impl$considerVanishedPlayers(final Player player, final Operation operation) { + if (player instanceof VanishableBridge vb) { + if (vb.bridge$vanishState().untargetable()) { + return false; + } + } + return operation.call(player); + } + + @WrapOperation(method = "lambda$static$0", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;is(Lnet/minecraft/tags/TagKey;)Z") + ) + private static boolean impl$considerVanishedEntitiesWithTyeps( + final LivingEntity entity, final TagKey> tagKey, final Operation operation + ) { + if (entity instanceof VanishableBridge vb) { + if (vb.bridge$vanishState().untargetable()) { + // this is inverted because we want to return true to signify the "entity type is not scary" + return true; + } + } + return operation.call(entity, tagKey); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/ParrotMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/parrot/ParrotMixin.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/ParrotMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/parrot/ParrotMixin.java index ba87ad79146..38dd761cfa7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/ParrotMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/parrot/ParrotMixin.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.animal; +package org.spongepowered.common.mixin.core.world.entity.animal.parrot; import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionHand; @@ -38,7 +38,7 @@ import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.mixin.core.world.entity.AgableMobMixin; -@Mixin(net.minecraft.world.entity.animal.Parrot.class) +@Mixin(net.minecraft.world.entity.animal.parrot.Parrot.class) public abstract class ParrotMixin extends AgableMobMixin { @Redirect(method = "mobInteract", diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/Rabbit_RaidGardenGoalMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/rabbit/Rabbit_RaidGardenGoalMixin.java similarity index 92% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/Rabbit_RaidGardenGoalMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/rabbit/Rabbit_RaidGardenGoalMixin.java index de97d0704e0..8f8cbe8ff08 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/Rabbit_RaidGardenGoalMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/rabbit/Rabbit_RaidGardenGoalMixin.java @@ -22,11 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.animal; +package org.spongepowered.common.mixin.core.world.entity.animal.rabbit; import net.minecraft.world.entity.PathfinderMob; import net.minecraft.world.entity.ai.goal.MoveToBlockGoal; -import net.minecraft.world.entity.animal.Rabbit; +import net.minecraft.world.entity.animal.rabbit.Rabbit; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -35,7 +35,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.bridge.world.entity.GrieferBridge; -@Mixin(targets = "net/minecraft/world/entity/animal/Rabbit$RaidGardenGoal") +@Mixin(targets = "net/minecraft/world/entity/animal/rabbit/Rabbit$RaidGardenGoal") public abstract class Rabbit_RaidGardenGoalMixin extends MoveToBlockGoal { // @formatter:off diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/WolfMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/wolf/WolfMixin.java similarity index 89% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/WolfMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/wolf/WolfMixin.java index 6730b52a314..316dc61ce0a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/WolfMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/animal/wolf/WolfMixin.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.animal; +package org.spongepowered.common.mixin.core.world.entity.animal.wolf; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.NeutralMob; @@ -44,7 +44,7 @@ import org.spongepowered.common.mixin.core.world.entity.AgableMobMixin; -@Mixin(net.minecraft.world.entity.animal.Wolf.class) +@Mixin(net.minecraft.world.entity.animal.wolf.Wolf.class) public abstract class WolfMixin extends AgableMobMixin implements AggressiveEntityBridge { // @formatter:off @@ -65,8 +65,8 @@ public abstract class WolfMixin extends AgableMobMixin implements AggressiveEnti @Redirect(method = "mobInteract", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Wolf;tryToTame(Lnet/minecraft/world/entity/player/Player;)V")) - private void impl$onTame(final net.minecraft.world.entity.animal.Wolf instance, final Player player, final Player $$0, final InteractionHand $$1) { + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/wolf/Wolf;tryToTame(Lnet/minecraft/world/entity/player/Player;)V")) + private void impl$onTame(final net.minecraft.world.entity.animal.wolf.Wolf instance, final Player player, final Player $$0, final InteractionHand $$1) { try { final ItemStack handStack = player.getItemInHand($$1); handStack.grow(1); @@ -80,14 +80,14 @@ public abstract class WolfMixin extends AgableMobMixin implements AggressiveEnti } @Inject(method = "tryToTame", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Wolf;tame(Lnet/minecraft/world/entity/player/Player;)V"), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/wolf/Wolf;tame(Lnet/minecraft/world/entity/player/Player;)V"), cancellable = true) private void impl$onTame(final Player player, final CallbackInfo ci) { try { PhaseTracker.getInstance().pushCause(player); if (SpongeCommon.post(SpongeEventFactory.createTameEntityEvent(PhaseTracker.getInstance().currentCause(), (Wolf) this))) { - this.shadow$level().broadcastEntityEvent((net.minecraft.world.entity.animal.Wolf) (Object) this, (byte)6); + this.shadow$level().broadcastEntityEvent((net.minecraft.world.entity.animal.wolf.Wolf) (Object) this, (byte)6); ci.cancel(); } } finally { diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/boss/enderdragon/EndCrystalMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/boss/enderdragon/EndCrystalMixin.java index d01d8504bcc..e544f39038f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/boss/enderdragon/EndCrystalMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/boss/enderdragon/EndCrystalMixin.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.mixin.core.world.entity.boss.enderdragon; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.boss.enderdragon.EndCrystal; @@ -31,9 +32,6 @@ import net.minecraft.world.level.Level; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.event.CauseStackManager; -import org.spongepowered.api.world.explosion.Explosion; -import org.spongepowered.api.world.server.ServerLocation; -import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -41,67 +39,61 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.bridge.explosives.ExplosiveBridge; import org.spongepowered.common.bridge.world.entity.boss.enderdragon.EndCrystalBridge; -import org.spongepowered.common.event.SpongeCommonEventFactory; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.mixin.core.world.entity.EntityMixin; import org.spongepowered.common.util.Constants; -import org.spongepowered.common.util.DamageEventUtil; import java.util.Optional; @Mixin(EndCrystal.class) public abstract class EndCrystalMixin extends EntityMixin implements ExplosiveBridge, EndCrystalBridge { - private int impl$explosionStrength = Constants.Entity.EnderCrystal.DEFAULT_EXPLOSION_STRENGTH; + private float impl$explosionRadius = Constants.Entity.EnderCrystal.DEFAULT_EXPLOSION_STRENGTH; // Explosive Impl @Override - public Optional bridge$getExplosionRadius() { - return Optional.of(this.impl$explosionStrength); + public Optional bridge$getExplosionRadius() { + return Optional.of(this.impl$explosionRadius); } @Override - public void bridge$setExplosionRadius(@Nullable final Integer radius) { - this.impl$explosionStrength = radius == null ? Constants.Entity.EnderCrystal.DEFAULT_EXPLOSION_STRENGTH : radius; + public void bridge$setExplosionRadius(final @Nullable Float radius) { + this.impl$explosionRadius = radius == null ? Constants.Entity.EnderCrystal.DEFAULT_EXPLOSION_STRENGTH : radius; } - @Redirect(method = "hurt", + @Redirect(method = "hurtServer", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;explode(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;Lnet/minecraft/world/level/ExplosionDamageCalculator;DDDFZLnet/minecraft/world/level/Level$ExplosionInteraction;)Lnet/minecraft/world/level/Explosion;" + target = "Lnet/minecraft/server/level/ServerLevel;explode(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;Lnet/minecraft/world/level/ExplosionDamageCalculator;DDDFZLnet/minecraft/world/level/Level$ExplosionInteraction;)V" ) ) - private net.minecraft.world.level.@Nullable Explosion impl$throwEventWithEntity(final net.minecraft.world.level.Level world, - final Entity entityIn, final DamageSource damageSource, final ExplosionDamageCalculator exDamageCalc, final double xIn, final double yIn, final double zIn, final float explosionRadius, - final boolean fire, final Level.ExplosionInteraction modeIn) { - // TODO fire? - // TODO smoking value correct? - return this.bridge$throwExplosionEventAndExplode(world, entityIn, xIn, yIn, zIn, modeIn.compareTo(Level.ExplosionInteraction.TNT) <= 0, damageSource); + private void impl$onHurtExplode( + final ServerLevel instance, final Entity entity, final DamageSource damageSource, + final ExplosionDamageCalculator explosionDamageCalculator, final double xIn, double yIn, double zIn, + float v, boolean b, Level.ExplosionInteraction explosionInteraction, final ServerLevel level, + final DamageSource source, final float amount) { + this.bridge$wrappedExplode(xIn, yIn, zIn, damageSource, source); } @Override - public net.minecraft.world.level.@Nullable Explosion bridge$throwExplosionEventAndExplode(final net.minecraft.world.level.Level world, - @Nullable final Entity nil, final double x, final double y, final double z, final boolean smoking, - @Nullable final DamageSource source) { + public void bridge$wrappedExplode(final double x, final double y, final double z, + @Nullable final DamageSource source, final DamageSource causeSource) { final CauseStackManager causeStackManager = PhaseTracker.getInstance(); try (final CauseStackManager.StackFrame frame = causeStackManager.pushCauseFrame()) { frame.pushCause(this); if (source != null) { - frame.pushCause(source); + frame.pushCause(causeSource); } - return SpongeCommonEventFactory.detonateExplosive(this, Explosion.builder() - .location(ServerLocation.of((ServerWorld) world, x, y, z)) - .radius(this.impl$explosionStrength) - .shouldPlaySmoke(smoking)) - .orElse(null); + this.level().explode( (Entity) (Object) this, source, null, x, y, z, this.impl$explosionRadius, false, Level.ExplosionInteraction.BLOCK); } } - @Inject(method = "hurt", cancellable = true, at = @At(value = "INVOKE", + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/boss/enderdragon/EndCrystal;remove(Lnet/minecraft/world/entity/Entity$RemovalReason;)V")) - private void attackImpl$onAttackEntityFrom(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { - if (DamageEventUtil.callOtherAttackEvent((Entity) (Object) this, source, amount).isCancelled()) { + private void attack$onHurt(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { + if (SpongeDamageTracker.callDamageEvents((org.spongepowered.api.entity.Entity) this, source, damage) == null) { cir.setReturnValue(true); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/boss/enderdragon/EnderDragonMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/boss/enderdragon/EnderDragonMixin.java index 6c0da06f9dc..b8374b0db17 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/boss/enderdragon/EnderDragonMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/boss/enderdragon/EnderDragonMixin.java @@ -24,11 +24,11 @@ */ package org.spongepowered.common.mixin.core.world.entity.boss.enderdragon; +import net.minecraft.tags.TagKey; import net.minecraft.world.entity.boss.enderdragon.EnderDragon; import net.minecraft.world.entity.boss.enderdragon.phases.DragonHoverPhase; import net.minecraft.world.entity.boss.enderdragon.phases.DragonPhaseInstance; import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import org.checkerframework.checker.nullness.qual.Nullable; @@ -53,23 +53,23 @@ public abstract class EnderDragonMixin extends MobMixin { method = "checkWalls", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/level/block/state/BlockState;getBlock()Lnet/minecraft/world/level/block/Block;" + target = "Lnet/minecraft/world/level/block/state/BlockState;is(Lnet/minecraft/tags/TagKey;)Z" ), slice = @Slice( from = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;getBlockState(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/block/state/BlockState;" + value = "FIELD", + target = "Lnet/minecraft/world/level/gamerules/GameRules;MOB_GRIEFING:Lnet/minecraft/world/level/gamerules/GameRule;" ), to = @At( - value = "FIELD", - target = "Lnet/minecraft/world/level/material/Material;FIRE:Lnet/minecraft/world/level/material/Material;", + value = "INVOKE", + target = "Lnet/minecraft/server/level/ServerLevel;removeBlock(Lnet/minecraft/core/BlockPos;Z)Z", opcode = Opcodes.GETSTATIC ) ), require = 0 // Forge rewrites the material request to block.isAir ) - private Block impl$onCanGrief(final BlockState state) { - return ((GrieferBridge) this).bridge$canGrief() ? state.getBlock() : Blocks.AIR; + private boolean impl$onCanGrief(final BlockState state, final TagKey tag) { + return ((GrieferBridge) this).bridge$canGrief() && state.is(tag); } /** diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/ArmorStandMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/ArmorStandMixin.java index 9e419a6e1e2..0788bb811e1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/ArmorStandMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/ArmorStandMixin.java @@ -24,6 +24,8 @@ */ package org.spongepowered.common.mixin.core.world.entity.decoration; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageTypes; @@ -32,6 +34,8 @@ import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.level.gameevent.GameEvent; import org.objectweb.asm.Opcodes; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.event.entity.DamageEntityEvent; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; @@ -43,29 +47,27 @@ import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.event.ShouldFire; import org.spongepowered.common.event.SpongeCommonEventFactory; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; import org.spongepowered.common.mixin.core.world.entity.LivingEntityMixin; -import org.spongepowered.common.util.DamageEventUtil; @Mixin(ArmorStand.class) public abstract class ArmorStandMixin extends LivingEntityMixin { // @formatter:off - @Shadow protected abstract void shadow$causeDamage(ServerLevel level, DamageSource damageSource, float damage); // damageArmorStand - @Shadow protected abstract void shadow$brokenByPlayer(final ServerLevel $$0, final DamageSource $$1); - + @Shadow protected abstract void shadow$causeDamage(ServerLevel level, DamageSource source, float damage); // damageArmorStand + @Shadow protected abstract void shadow$brokenByPlayer(final ServerLevel level, final DamageSource source); // @formatter:on /** * The return value is set to false if the entity should not be completely destroyed. */ private void impl$callDamageBeforeKill(final DamageSource source, final CallbackInfoReturnable cir) { - var event = DamageEventUtil.callSimpleDamageEntityEvent(source, (ArmorStand) (Object) this, Math.max(1000, this.shadow$getHealth())); - if (event.isCancelled()) { + final DamageEntityEvent.Post event = SpongeDamageTracker.callDamageEvents((Entity) this, source, this.shadow$getHealth()); + if (event == null) { cir.setReturnValue(false); - } - if (event.finalDamage() < this.shadow$getHealth()) { // Deal reduced damage? + } else if (event.finalDamage() < this.shadow$getHealth()) { // Deal reduced damage? this.shadow$causeDamage((ServerLevel) this.shadow$level(), source, (float) event.finalDamage()); cir.setReturnValue(false); } @@ -75,20 +77,20 @@ public abstract class ArmorStandMixin extends LivingEntityMixin { /** * BYPASSES_INVULNERABILITY */ - @Inject(method = "hurt", cancellable = true, + @Inject(method = "hurtServer", cancellable = true, slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/tags/DamageTypeTags;BYPASSES_INVULNERABILITY:Lnet/minecraft/tags/TagKey;")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;kill()V", ordinal = 0)) - private void impl$fireDamageEventOutOfWorld(final DamageSource source, final float $$1, final CallbackInfoReturnable cir) { + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;kill(Lnet/minecraft/server/level/ServerLevel;)V", ordinal = 0)) + private void impl$fireDamageEventOutOfWorld(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { this.impl$callDamageBeforeKill(source, cir); } /** * IS_EXPLOSION */ - @Inject(method = "hurt", cancellable = true, + @Inject(method = "hurtServer", cancellable = true, slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/tags/DamageTypeTags;IS_EXPLOSION:Lnet/minecraft/tags/TagKey;")), at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;brokenByAnything(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;)V")) - private void impl$fireDamageEventExplosion(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { + private void impl$fireDamageEventExplosion(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { this.impl$callDamageBeforeKill(source, cir); } @@ -97,43 +99,43 @@ public abstract class ArmorStandMixin extends LivingEntityMixin { * IGNITES_ARMOR_STANDS + isOnFire * BURNS_ARMOR_STANDS + health > 0.5 */ - @Redirect(method = "hurt", + @WrapOperation(method = "hurtServer", slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/tags/DamageTypeTags;IGNITES_ARMOR_STANDS:Lnet/minecraft/tags/TagKey;")), at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;causeDamage(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;F)V")) - private void impl$fireDamageEventDamage(final ArmorStand self, final ServerLevel level, final DamageSource source, final float amount) { - var event = DamageEventUtil.callSimpleDamageEntityEvent(source, self, amount); - if (!event.isCancelled()) { - this.shadow$causeDamage(level, source, (float) event.finalDamage()); + private void impl$fireDamageEventDamage(final ArmorStand self, final ServerLevel level, final DamageSource source, final float damage, final Operation operation) { + final DamageEntityEvent.Post event = SpongeDamageTracker.callDamageEvents((Entity) this, source, damage); + if (event != null) { + operation.call(self, level, source, (float) event.finalDamage()); } } /** * {@link DamageSource#isCreativePlayer()} */ - @Inject(method = "hurt", + @Inject(method = "hurtServer", slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/DamageSource;isCreativePlayer()Z")), at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;playBrokenSound()V"), cancellable = true) - private void impl$fireDamageEventCreativePunch(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { + private void impl$fireDamageEventCreativePunch(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { this.impl$callDamageBeforeKill(source, cir); } /** * {@link ArmorStand#lastHit} was not recently */ - @Inject(method = "hurt", cancellable = true, + @Inject(method = "hurtServer", cancellable = true, slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;lastHit:J", opcode = Opcodes.GETFIELD)), at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;broadcastEntityEvent(Lnet/minecraft/world/entity/Entity;B)V")) - private void impl$fireDamageEventFirstPunch(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { + private void impl$fireDamageEventFirstPunch(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { // While this doesn't technically "damage" the armor stand, it feels like damage in other respects, so fire an event. - var event = DamageEventUtil.callSimpleDamageEntityEvent(source, (ArmorStand) (Object) this, 0); - if (event.isCancelled()) { + final DamageEntityEvent.Post event = SpongeDamageTracker.callDamageEvents((Entity) this, source, 0); + if (event == null) { cir.setReturnValue(false); } } - @Inject(method = "hurt", cancellable = true, at = @At(value = "INVOKE", + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;brokenByPlayer(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;)V")) - private void impl$beforeBrokenByPlayer(final DamageSource $$0, final float $$1, final CallbackInfoReturnable cir) { + private void impl$beforeBrokenByPlayer(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { if (ShouldFire.DESTRUCT_ENTITY_EVENT && !((LevelBridge) this.shadow$level()).bridge$isFake()) { final var event = SpongeCommonEventFactory.callDestructEntityEventDeath((ArmorStand) (Object) this, null); if (event.isCancelled()) { @@ -143,35 +145,34 @@ public abstract class ArmorStandMixin extends LivingEntityMixin { } } - @Redirect(method = "hurt", at = @At(value = "INVOKE", + @Redirect(method = "hurtServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;brokenByPlayer(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;)V")) - public void impl$onBrokenByPlayer(final ArmorStand instance, final ServerLevel $$0, final DamageSource $$1) - { - try (final EffectTransactor ignored = PhaseTracker.SERVER.getPhaseContext().getTransactor().ensureEntityDropTransactionEffect((LivingEntity) (Object) this)) { - this.shadow$brokenByPlayer($$0, $$1); + public void impl$onBrokenByPlayer(final ArmorStand self, final ServerLevel level, final DamageSource source) { + try (final EffectTransactor ignored = PhaseTracker.getWorldInstance(level).getPhaseContext().getTransactor().ensureEntityDropTransactionEffect((LivingEntity) (Object) this)) { + this.shadow$brokenByPlayer(level, source); } } /** * {@link ArmorStand#lastHit} was recently */ - @Inject(method = "hurt", cancellable = true, at = @At(value = "INVOKE", + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;brokenByPlayer(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;)V")) - private void impl$fireDamageEventSecondPunch(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { + private void impl$fireDamageEventSecondPunch(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { this.impl$callDamageBeforeKill(source, cir); } /** - * To avoid a loop between {@link #kill} and {@link ArmorStand#hurt}, - * we make sure that any killing within the {@link ArmorStand#hurt} + * To avoid a loop between {@link #kill} and {@link ArmorStand#hurtServer}, + * we make sure that any killing within the {@link ArmorStand#hurtServer} * method calls this instead of the {@link Overwrite} * - * @param target the killed stand + * @param self the killed stand */ - @Redirect(method = "hurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;kill()V")) - private void impl$actuallyKill(final ArmorStand target) { - target.remove(RemovalReason.KILLED); - target.gameEvent(GameEvent.ENTITY_DIE); + @Redirect(method = "hurtServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;kill(Lnet/minecraft/server/level/ServerLevel;)V")) + private void impl$actuallyKill(final ArmorStand self, final ServerLevel level) { + self.remove(RemovalReason.KILLED); + self.gameEvent(GameEvent.ENTITY_DIE); } /** @@ -179,12 +180,12 @@ public abstract class ArmorStandMixin extends LivingEntityMixin { * we make sure that any killing within the {@link #shadow$causeDamage} * method calls this instead of the {@link Overwrite} * - * @param target the killed stand + * @param self the killed stand */ - @Redirect(method = "causeDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;kill()V")) - private void impl$actuallyKill2(final ArmorStand target) { - target.remove(RemovalReason.KILLED); - target.gameEvent(GameEvent.ENTITY_DIE); + @Redirect(method = "causeDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/decoration/ArmorStand;kill(Lnet/minecraft/server/level/ServerLevel;)V")) + private void impl$actuallyKill2(final ArmorStand self, final ServerLevel level) { + self.remove(RemovalReason.KILLED); + self.gameEvent(GameEvent.ENTITY_DIE); } /** @@ -192,12 +193,12 @@ public abstract class ArmorStandMixin extends LivingEntityMixin { * @reason EntityArmorStand "simplifies" this method to simply call {@link * #shadow$remove(RemovalReason)}. However, this ignores our custom event. * Instead, delegate to the superclass causing it to use - * {@link ArmorStand#hurt(DamageSource, float)} with {@link DamageTypes#GENERIC_KILL} + * {@link ArmorStand#hurtServer} with {@link DamageTypes#GENERIC_KILL} * * This needs to be reimplemented in {@link #impl$actuallyKill}! */ @Overwrite - public void kill() { - super.shadow$kill(); + public void kill(final ServerLevel level) { + super.shadow$kill(level); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/BlockAttachedEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/BlockAttachedEntityMixin.java index 03a8ad46608..8ae562e80a5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/BlockAttachedEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/BlockAttachedEntityMixin.java @@ -24,17 +24,18 @@ */ package org.spongepowered.common.mixin.core.world.entity.decoration; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.decoration.BlockAttachedEntity; +import org.spongepowered.api.entity.Entity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; import org.spongepowered.common.mixin.core.world.entity.EntityMixin; -import org.spongepowered.common.util.DamageEventUtil; @Mixin(BlockAttachedEntity.class) public abstract class BlockAttachedEntityMixin extends EntityMixin { @@ -54,10 +55,10 @@ public abstract class BlockAttachedEntityMixin extends EntityMixin { return this.shadow$survives() && !this.impl$ignorePhysics; } - @Inject(method = "hurt", cancellable = true, at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/decoration/BlockAttachedEntity;kill()V")) - private void attackImpl$postEventOnAttackEntityFrom(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { - if (DamageEventUtil.callOtherAttackEvent((Entity) (Object) this, source, amount).isCancelled()) { + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/decoration/BlockAttachedEntity;kill(Lnet/minecraft/server/level/ServerLevel;)V")) + private void attackImpl$postEventOnAttackEntityFrom(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { + if (SpongeDamageTracker.callDamageEvents((Entity) this, source, damage) == null) { cir.setReturnValue(true); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/ItemFrameMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/ItemFrameMixin.java index f84c4d54c9b..0ffd03487b8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/ItemFrameMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/ItemFrameMixin.java @@ -24,21 +24,22 @@ */ package org.spongepowered.common.mixin.core.world.entity.decoration; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; +import org.spongepowered.api.entity.Entity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.common.util.DamageEventUtil; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; @Mixin(net.minecraft.world.entity.decoration.ItemFrame.class) public abstract class ItemFrameMixin extends HangingEntityMixin { - @Inject(method = "hurt", cancellable = true, at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/decoration/ItemFrame;dropItem(Lnet/minecraft/world/entity/Entity;Z)V")) - private void attackImpl$onAttackEntityFrom(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { - if (DamageEventUtil.callOtherAttackEvent((Entity) (Object) this, source, amount).isCancelled()) { + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/decoration/ItemFrame;dropItem(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/Entity;Z)V")) + private void attack$onHurt(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { + if (SpongeDamageTracker.callDamageEvents((Entity) this, source, damage) == null) { cir.setReturnValue(true); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/MannequinMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/MannequinMixin.java new file mode 100644 index 00000000000..10b5feed971 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/decoration/MannequinMixin.java @@ -0,0 +1,34 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity.decoration; + +import org.spongepowered.api.entity.Mannequin; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.mixin.core.world.entity.AvatarMixin; + +@Mixin(net.minecraft.world.entity.decoration.Mannequin.class) +public abstract class MannequinMixin extends AvatarMixin implements Mannequin { + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/item/ItemEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/item/ItemEntityMixin.java index d1808c0f028..4a1984f08c0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/item/ItemEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/item/ItemEntityMixin.java @@ -24,11 +24,12 @@ */ package org.spongepowered.common.mixin.core.world.entity.item; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.ItemStack; import org.spongepowered.api.Sponge; +import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.Item; import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.SpongeEventFactory; @@ -46,10 +47,10 @@ import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.config.SpongeGameConfigs; import org.spongepowered.common.data.provider.entity.ItemData; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.mixin.core.world.entity.EntityMixin; import org.spongepowered.common.util.Constants; -import org.spongepowered.common.util.DamageEventUtil; @Mixin(ItemEntity.class) public abstract class ItemEntityMixin extends EntityMixin implements ItemEntityBridge { @@ -69,7 +70,7 @@ public abstract class ItemEntityMixin extends EntityMixin implements ItemEntityB @ModifyConstant(method = "mergeWithNeighbours", constant = @Constant(doubleValue = Constants.Entity.Item.DEFAULT_ITEM_MERGE_RADIUS)) private double impl$changeSearchRadiusFromConfig(final double originalRadius) { - if (this.shadow$level().isClientSide || ((LevelBridge) this.shadow$level()).bridge$isFake()) { + if (this.shadow$level().isClientSide() || ((LevelBridge) this.shadow$level()).bridge$isFake()) { return originalRadius; } if (this.impl$cachedRadius == -1) { @@ -116,12 +117,12 @@ public abstract class ItemEntityMixin extends EntityMixin implements ItemEntityB method = "tick()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/item/ItemEntity;discard()V"), slice = @Slice( - from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/item/ItemEntity;updateInWaterStateAndDoFluidPushing()Z"), + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/item/ItemEntity;updateFluidInteraction()Z"), to = @At("TAIL") ) ) private void impl$fireExpireEntityEventTargetItem(final CallbackInfo ci) { - if (!PhaseTracker.SERVER.onSidedThread() || this.shadow$getItem().isEmpty()) { + if (!PhaseTracker.getWorldInstance((ServerLevel) this.shadow$level()).onSidedThread() || this.shadow$getItem().isEmpty()) { // In the rare case the first if block is actually at the end of the method instruction list, we don't want to // erroneously be calling this twice. return; @@ -143,10 +144,10 @@ public abstract class ItemEntityMixin extends EntityMixin implements ItemEntityB } } - @Inject(method = "hurt", cancellable = true, at = @At(value = "INVOKE", + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/item/ItemEntity;markHurt()V")) - private void attackImpl$onAttackEntityFrom(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { - if (DamageEventUtil.callOtherAttackEvent((Entity) (Object) this, source, amount).isCancelled()) { + private void attack$onHurt(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { + if (SpongeDamageTracker.callDamageEvents((Entity) this, source, damage) == null) { cir.setReturnValue(true); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/item/PrimedTntMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/item/PrimedTntMixin.java index a0e2a852b8b..d8e8c9a6646 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/item/PrimedTntMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/item/PrimedTntMixin.java @@ -24,30 +24,18 @@ */ package org.spongepowered.common.mixin.core.world.entity.item; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.item.PrimedTnt; -import net.minecraft.world.level.Explosion.BlockInteraction; -import net.minecraft.world.level.ExplosionDamageCalculator; -import net.minecraft.world.level.Level; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.data.Keys; -import org.spongepowered.api.entity.explosive.fused.PrimedTNT; import org.spongepowered.api.event.CauseStackManager; -import org.spongepowered.api.world.explosion.Explosion; -import org.spongepowered.api.world.server.ServerLocation; -import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.bridge.explosives.ExplosiveBridge; import org.spongepowered.common.bridge.explosives.FusedExplosiveBridge; import org.spongepowered.common.bridge.world.entity.item.PrimedTntBridge; -import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.mixin.core.world.entity.EntityMixin; import org.spongepowered.common.util.Constants; @@ -60,10 +48,11 @@ public abstract class PrimedTntMixin extends EntityMixin implements PrimedTntBri // @formatter:off @Shadow public abstract int shadow$getFuse(); @Shadow public abstract void shadow$setFuse(int var1); + @Shadow private float explosionPower; + // @formatter:on @Nullable private LivingEntity impl$detonator; - private int bridge$explosionRadius = Constants.Entity.PrimedTNT.DEFAULT_EXPLOSION_RADIUS; private int bridge$fuseDuration = Constants.Entity.PrimedTNT.DEFAULT_FUSE_DURATION; private boolean impl$postPrimeTriggered = false; @@ -78,13 +67,13 @@ public abstract class PrimedTntMixin extends EntityMixin implements PrimedTntBri } @Override - public Optional bridge$getExplosionRadius() { - return Optional.of(this.bridge$explosionRadius); + public Optional bridge$getExplosionRadius() { + return Optional.of(this.explosionPower); } @Override - public void bridge$setExplosionRadius(@Nullable final Integer radius) { - this.bridge$explosionRadius = radius == null ? Constants.Entity.PrimedTNT.DEFAULT_EXPLOSION_RADIUS : radius; + public void bridge$setExplosionRadius(final @Nullable Float radius) { + this.explosionPower = radius == null ? Constants.Entity.PrimedTNT.DEFAULT_EXPLOSION_RADIUS : radius; } @Override @@ -113,37 +102,9 @@ public abstract class PrimedTntMixin extends EntityMixin implements PrimedTntBri this.shadow$setFuse(fuseTicks); } - @Redirect( - method = "explode", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;explode(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;Lnet/minecraft/world/level/ExplosionDamageCalculator;DDDFZLnet/minecraft/world/level/Level$ExplosionInteraction;)Lnet/minecraft/world/level/Explosion;" - ) - ) - private net.minecraft.world.level.@Nullable Explosion impl$useSpongeExplosion(final Level world, - final Entity entityIn, - final DamageSource damageSource, - final ExplosionDamageCalculator explosionDamageCalculator, - final double xIn, final double yIn, final double zIn, final - float explosionRadius, - final boolean falseValue, - final Level.ExplosionInteraction modeIn) { - return SpongeCommonEventFactory.detonateExplosive(this, Explosion.builder() - .location(ServerLocation.of((ServerWorld) world, xIn, yIn, zIn)) - .sourceExplosive((PrimedTNT) this) - .radius(this.bridge$explosionRadius) - .shouldPlaySmoke(modeIn.ordinal() > BlockInteraction.KEEP.ordinal()) - .shouldBreakBlocks(modeIn.ordinal() > BlockInteraction.KEEP.ordinal())) - .orElseGet(() -> { - ((PrimedTNT) this).offer(Keys.IS_PRIMED, false); - return null; - }); - } - - @Inject(method = "tick()V", at = @At("RETURN")) private void impl$updateTNTPushPrime(final CallbackInfo ci) { - if (!this.impl$postPrimeTriggered && !this.shadow$level().isClientSide) { + if (!this.impl$postPrimeTriggered && !this.shadow$level().isClientSide()) { this.impl$postPrimeTriggered = true; try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { if (this.impl$detonator != null) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/monster/CreeperMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/monster/CreeperMixin.java index d836d7e70dc..8c28d0a71bc 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/monster/CreeperMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/monster/CreeperMixin.java @@ -24,16 +24,10 @@ */ package org.spongepowered.common.mixin.core.world.entity.monster; -import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.entity.living.monster.Creeper; -import org.spongepowered.api.world.explosion.Explosion; -import org.spongepowered.api.world.server.ServerLocation; -import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -42,8 +36,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.bridge.explosives.ExplosiveBridge; import org.spongepowered.common.bridge.explosives.FusedExplosiveBridge; -import org.spongepowered.common.bridge.world.entity.GrieferBridge; -import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.util.Constants; import java.util.Optional; @@ -68,13 +60,13 @@ public abstract class CreeperMixin extends MonsterMixin implements FusedExplosiv // FusedExplosive Impl @Override - public Optional bridge$getExplosionRadius() { - return Optional.of(this.explosionRadius); + public Optional bridge$getExplosionRadius() { + return Optional.of((float) this.explosionRadius); } @Override - public void bridge$setExplosionRadius(@Nullable final Integer radius) { - this.explosionRadius = radius == null ? Constants.Entity.Creeper.DEFAULT_EXPLOSION_RADIUS : radius; + public void bridge$setExplosionRadius(final @Nullable Float radius) { + this.explosionRadius = radius == null ? Constants.Entity.Creeper.DEFAULT_EXPLOSION_RADIUS : radius.intValue(); } @Override @@ -109,7 +101,7 @@ public abstract class CreeperMixin extends MonsterMixin implements FusedExplosiv @Inject(method = "setSwellDir", at = @At("HEAD"), cancellable = true) private void impl$preStateChange(final int state, final CallbackInfo ci) { - if (this.shadow$level().isClientSide) { + if (this.shadow$level().isClientSide()) { return; } @@ -126,7 +118,7 @@ public abstract class CreeperMixin extends MonsterMixin implements FusedExplosiv @Inject(method = "setSwellDir", at = @At("RETURN")) private void impl$postStateChange(final int state, final CallbackInfo ci) { - if (this.shadow$level().isClientSide) { + if (this.shadow$level().isClientSide()) { return; } @@ -140,25 +132,16 @@ public abstract class CreeperMixin extends MonsterMixin implements FusedExplosiv } } - @Redirect(method = "explodeCreeper", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;explode(Lnet/minecraft/world/entity/Entity;DDDFLnet/minecraft/world/level/Level$ExplosionInteraction;)Lnet/minecraft/world/level/Explosion;")) - private net.minecraft.world.level.@Nullable Explosion impl$useSpongeExplosion(final net.minecraft.world.level.Level world, final Entity self, final double x, - final double y, final double z, final float strength, final Level.ExplosionInteraction mode) { - return SpongeCommonEventFactory.detonateExplosive(this, Explosion.builder() - .location(ServerLocation.of((ServerWorld) world, x, y, z)) - .sourceExplosive(((Creeper) this)) - .radius(strength) - .shouldPlaySmoke(mode != Level.ExplosionInteraction.NONE) - .shouldBreakBlocks(mode != Level.ExplosionInteraction.NONE && ((GrieferBridge) this).bridge$canGrief())) - .orElseGet(() -> { - this.impl$detonationCancelled = true; - return null; - }); + @Override + public void bridge$cancelExplosion() { + this.impl$detonationCancelled = true; } @Inject(method = "explodeCreeper", at = @At("RETURN")) private void impl$postExplode(final CallbackInfo ci) { if (this.impl$detonationCancelled) { this.impl$detonationCancelled = this.dead = false; + this.shadow$unsetRemoved(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/monster/ZombifiedPiglinMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/monster/zombie/ZombifiedPiglinMixin.java similarity index 85% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/monster/ZombifiedPiglinMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/monster/zombie/ZombifiedPiglinMixin.java index 35a889a05ac..4f617112c41 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/monster/ZombifiedPiglinMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/monster/zombie/ZombifiedPiglinMixin.java @@ -22,19 +22,20 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.monster; +package org.spongepowered.common.mixin.core.world.entity.monster.zombie; import net.minecraft.world.entity.NeutralMob; -import net.minecraft.world.entity.monster.ZombifiedPiglin; +import net.minecraft.world.entity.monster.zombie.ZombifiedPiglin; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.bridge.world.entity.AggressiveEntityBridge; +import org.spongepowered.common.mixin.core.world.entity.monster.MonsterMixin; @Mixin(ZombifiedPiglin.class) public abstract class ZombifiedPiglinMixin extends MonsterMixin implements AggressiveEntityBridge { // @formatter:off - @Shadow private int remainingPersistentAngerTime; + @Shadow private long persistentAngerEndTime; // @formatter:on @Override @@ -45,9 +46,9 @@ public abstract class ZombifiedPiglinMixin extends MonsterMixin implements Aggre @Override public void bridge$setAngry(boolean angry) { if (angry) { - this.remainingPersistentAngerTime = 400 + this.random.nextInt(400); + this.persistentAngerEndTime = 400 + this.random.nextInt(400); } else { - this.remainingPersistentAngerTime = 0; + this.persistentAngerEndTime = 0; } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/VillagerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/VillagerMixin.java deleted file mode 100644 index e34c6aeeabe..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/VillagerMixin.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.entity.npc; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.npc.Villager; -import org.slf4j.Logger; -import org.spongepowered.api.Sponge; -import org.spongepowered.api.block.BlockSnapshot; -import org.spongepowered.api.entity.living.Living; -import org.spongepowered.api.event.Cause; -import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.world.server.ServerWorld; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.common.event.tracking.PhaseTracker; - -@Mixin(Villager.class) -public abstract class VillagerMixin extends AbstractVillagerMixin { - - @Redirect(method = "die", - at = @At(value = "INVOKE", - target = "Lorg/slf4j/Logger;info(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", - remap = false - ) - ) - private void impl$silenceMojangSpam(final Logger logger, final String message, final Object p0, final Object p1) { - //noop - } - - @Inject(method = "startSleeping", at = @At("HEAD"), cancellable = true) - private void impl$callPreSleepingEvent(BlockPos param0, CallbackInfo ci) { - final Cause currentCause = PhaseTracker.getInstance().currentCause(); - final BlockSnapshot snapshot = ((ServerWorld) this.shadow$level()).createSnapshot(param0.getX(), param0.getY(), param0.getZ()); - if (Sponge.eventManager().post(SpongeEventFactory.createSleepingEventPre(currentCause, snapshot, (Living) this))) { - ci.cancel(); - } - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/AbstractVillagerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/villager/AbstractVillagerMixin.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/AbstractVillagerMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/villager/AbstractVillagerMixin.java index 9bd38e94a91..ecfd4220a5a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/AbstractVillagerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/villager/AbstractVillagerMixin.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.npc; +package org.spongepowered.common.mixin.core.world.entity.npc.villager; -import net.minecraft.world.entity.npc.AbstractVillager; +import net.minecraft.world.entity.npc.villager.AbstractVillager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.mixin.core.world.entity.AgableMobMixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/villager/VillagerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/villager/VillagerMixin.java new file mode 100644 index 00000000000..67310f78691 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/villager/VillagerMixin.java @@ -0,0 +1,46 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity.npc.villager; + +import net.minecraft.world.entity.npc.villager.Villager; +import org.slf4j.Logger; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(Villager.class) +public abstract class VillagerMixin extends AbstractVillagerMixin { + + @Redirect(method = "die", + at = @At(value = "INVOKE", + target = "Lorg/slf4j/Logger;info(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", + remap = false + ) + ) + private void impl$silenceMojangSpam(final Logger logger, final String message, final Object p0, final Object p1) { + //noop + } + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/WanderingTraderMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/wanderingtrader/WanderingTraderMixin.java similarity index 83% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/WanderingTraderMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/wanderingtrader/WanderingTraderMixin.java index c083ec4f6d9..c8afe7fcfbf 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/WanderingTraderMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/npc/wanderingtrader/WanderingTraderMixin.java @@ -22,14 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.npc; +package org.spongepowered.common.mixin.core.world.entity.npc.wanderingtrader; -import net.minecraft.world.entity.npc.WanderingTraderSpawner; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.npc.wanderingtrader.WanderingTraderSpawner; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.util.Constants; @Mixin(WanderingTraderSpawner.class) @@ -40,9 +41,11 @@ public abstract class WanderingTraderMixin { // @formatter:on @Inject(method = "tick", at = @At("HEAD"), cancellable = true) - private void impl$checkInfiniteTickDelay(final CallbackInfoReturnable cir) { + private void impl$checkInfiniteTickDelay( + final ServerLevel $$0, final boolean $$1, final CallbackInfo cir + ) { if (this.tickDelay == Constants.TickConversions.INFINITE_TICKS) { - cir.setReturnValue(0); + cir.cancel(); } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java index bcfb69c8798..30c7c390251 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java @@ -28,19 +28,18 @@ import com.mojang.datafixers.util.Either; import net.kyori.adventure.bossbar.BossBar; import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; -import net.minecraft.network.syncher.EntityDataAccessor; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.PermissionSet; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundSource; import net.minecraft.util.Unit; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player.BedSleepingProblem; import net.minecraft.world.food.FoodData; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -49,9 +48,8 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.pattern.BlockInWorld; import net.minecraft.world.phys.AABB; -import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.phys.Vec3; import net.minecraft.world.scores.Team; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.entity.living.Living; import org.spongepowered.api.event.CauseStackManager; @@ -61,6 +59,7 @@ import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -79,7 +78,7 @@ import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.item.util.ItemStackUtil; -import org.spongepowered.common.mixin.core.world.entity.LivingEntityMixin; +import org.spongepowered.common.mixin.core.world.entity.AvatarMixin; import org.spongepowered.common.util.Constants; import org.spongepowered.common.util.ExperienceHolderUtil; @@ -89,31 +88,29 @@ import java.util.Set; @Mixin(net.minecraft.world.entity.player.Player.class) -public abstract class PlayerMixin extends LivingEntityMixin implements PlayerBridge, GameProfileHolderBridge, ViewerBridge { +public abstract class PlayerMixin extends AvatarMixin implements PlayerBridge, GameProfileHolderBridge, ViewerBridge { // @formatter: off - @Shadow @Final protected static EntityDataAccessor DATA_PLAYER_MODE_CUSTOMISATION; @Shadow public int experienceLevel; @Shadow public int totalExperience; @Shadow public float experienceProgress; @Shadow public AbstractContainerMenu containerMenu; @Shadow @Final public InventoryMenu inventoryMenu; - @Shadow @Final private GameProfile gameProfile; + @Shadow @Final @Mutable private GameProfile gameProfile; @Shadow public abstract boolean shadow$isSpectator(); @Shadow public abstract int shadow$getXpNeededForNextLevel(); - @Shadow @Nullable public abstract ItemEntity shadow$drop(final ItemStack droppedItem, final boolean dropAround, final boolean traceItem); @Shadow public abstract FoodData shadow$getFoodData(); - @Shadow public abstract Scoreboard shadow$getScoreboard(); @Shadow public abstract boolean shadow$isCreative(); @Shadow public abstract String shadow$getScoreboardName(); - @Shadow public abstract void shadow$awardStat(ResourceLocation stat); + @Shadow public abstract void shadow$awardStat(Identifier stat); @Shadow public abstract Inventory shadow$getInventory(); + @Shadow public abstract PermissionSet shadow$permissions(); @Shadow public Either shadow$startSleepInBed(final BlockPos param0) { return null; // Shadowed } - @Shadow protected abstract void shadow$playShoulderEntityAmbientSound(CompoundTag p_192028_1_); // @formatter: on + private boolean impl$affectsSpawning = true; protected final boolean impl$isFake = this.bridge$isFakePlayer(); @@ -135,6 +132,11 @@ public abstract class PlayerMixin extends LivingEntityMixin implements PlayerBri return this.gameProfile; } + @Override + public void bridge$setGameProfile(final GameProfile profile) { + this.gameProfile = profile; + } + @Override public int bridge$getExperienceSinceLevel() { return this.totalExperience - ExperienceHolderUtil.xpAtLevel(this.experienceLevel); @@ -204,33 +206,18 @@ public abstract class PlayerMixin extends LivingEntityMixin implements PlayerBri return playerEntity.getName(); } - @Redirect( - method = "aiStep", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/entity/player/Player;playShoulderEntityAmbientSound(Lnet/minecraft/nbt/CompoundTag;)V" - ) - ) - private void impl$ignoreShoulderSoundsWhileVanished(final net.minecraft.world.entity.player.Player thisPlayer, final CompoundTag tag) { - if (!this.bridge$vanishState().createsSounds()) { - return; - } - this.shadow$playShoulderEntityAmbientSound(tag); - } - @Redirect( method = { "playSound(Lnet/minecraft/sounds/SoundEvent;FF)V", "giveExperienceLevels(I)V", - "eat(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/food/FoodProperties;)Lnet/minecraft/world/item/ItemStack;" }, at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V" + target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/Entity;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V" ) ) private void impl$ignoreExperienceLevelSoundsWhileVanished(final Level world, - final net.minecraft.world.entity.player.Player player, final double x, final double y, final double z, + final Entity player, final double x, final double y, final double z, final SoundEvent sound, final SoundSource category, final float volume, final float pitch ) { if (!this.bridge$vanishState().createsSounds()) { @@ -239,12 +226,12 @@ public abstract class PlayerMixin extends LivingEntityMixin implements PlayerBri this.shadow$level().playSound(player, x, y, z, sound, category, volume, pitch); } - @Redirect(method = "canUseGameMasterBlocks", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getPermissionLevel()I")) - private int impl$checkPermissionForCommandBlock(final net.minecraft.world.entity.player.Player playerEntity) { + @Redirect(method = "canUseGameMasterBlocks", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;permissions()Lnet/minecraft/server/permissions/PermissionSet;")) + private PermissionSet impl$checkPermissionForCommandBlock(final Player playerEntity) { if (this instanceof Subject) { - return ((Subject) this).hasPermission(Constants.Permissions.COMMAND_BLOCK_PERMISSION) ? Constants.Permissions.COMMAND_BLOCK_LEVEL : 0; + return ((Subject) this).hasPermission(Constants.Permissions.COMMAND_BLOCK_PERMISSION) ? PermissionSet.ALL_PERMISSIONS : PermissionSet.NO_PERMISSIONS; } else { - return this.shadow$getPermissionLevel(); + return this.shadow$permissions(); } } @@ -300,7 +287,8 @@ public abstract class PlayerMixin extends LivingEntityMixin implements PlayerBri } @Inject(method = "interactOn", at = @At(value = "HEAD"), cancellable = true) - protected void impl$onRightClickEntity(final Entity entityToInteractOn, final InteractionHand hand, + protected void impl$onRightClickEntity( + final Entity entity, final InteractionHand hand, final Vec3 location, final CallbackInfoReturnable cir) { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack.java new file mode 100644 index 00000000000..a2f2f028959 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack.java @@ -0,0 +1,337 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity.player; + +import com.llamalad7.mixinextras.expression.Definition; +import com.llamalad7.mixinextras.expression.Expression; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Cancellable; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; +import org.spongepowered.api.event.entity.AttackEntityEvent; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.bridge.world.entity.TrackedAttackBridge; +import org.spongepowered.common.event.cause.entity.damage.SpongeAttackTracker; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageStep; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; +import org.spongepowered.common.mixin.core.world.entity.LivingEntityMixin_Damage; + +import java.util.Deque; +import java.util.LinkedList; + +@SuppressWarnings("ConstantConditions") +@Mixin(value = Player.class, priority = 900) +public abstract class PlayerMixin_Attack extends LivingEntityMixin_Damage implements TrackedAttackBridge { + + //@formatter:off + @Shadow protected abstract float shadow$getEnchantedDamage(final Entity target, final float damage, final DamageSource source); + @Shadow @Final public InventoryMenu inventoryMenu; + //@formatter:on + + private final Deque attack$trackers = new LinkedList<>(); + + @Override + public final @Nullable SpongeAttackTracker attack$tracker() { + return this.attack$trackers.peekLast(); + } + + @Definition(id = "getEnchantedDamage", method = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F") + @Expression("? = ? * (this.getEnchantedDamage(?, @(?), ?) - ?)") + @ModifyExpressionValue(method = "attack", at = @At("MIXINEXTRAS:EXPRESSION")) + private float attack$firePreEvent(final float damage, @Local(argsOnly = true) final Entity target, @Local final DamageSource source, @Local final ItemStack weapon, final @Cancellable CallbackInfo ci) { + final SpongeAttackTracker tracker = SpongeAttackTracker.callAttackPreEvent((org.spongepowered.api.entity.Entity) target, source, damage, weapon); + if (tracker == null) { + ci.cancel(); + return damage; + } + + this.attack$trackers.addLast(tracker); + return tracker.applyStartStep(); + } + + @ModifyExpressionValue(method = "attack", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getAttackStrengthScale(F)F")) + private float attack$captureAttackStrength(final float value) { + final SpongeAttackTracker tracker = this.attack$tracker(); + if (tracker != null) { + tracker.setAttackStrength(value); + } + return value; + } + + @Definition(id = "baseDamageScaleFactor", method = "Lnet/minecraft/world/entity/player/Player;baseDamageScaleFactor()F") + @Expression("? = @(?) * this.baseDamageScaleFactor()") + @ModifyExpressionValue(method = "attack", at = @At("MIXINEXTRAS:EXPRESSION")) + private float attack$modifyBeforeBaseCooldown(final float damage) { + final SpongeAttackTracker tracker = this.attack$tracker(); + return tracker == null ? damage : tracker.startStep(DamageStepTypes.BASE_COOLDOWN, damage, this); + } + + @Definition(id = "baseDamageScaleFactor", method = "Lnet/minecraft/world/entity/player/Player;baseDamageScaleFactor()F") + @Expression("? = @(? * this.baseDamageScaleFactor())") + @ModifyExpressionValue(method = "attack", at = @At("MIXINEXTRAS:EXPRESSION")) + private float attack$modifyAfterBaseCooldown(final float damage) { + final SpongeAttackTracker tracker = this.attack$tracker(); + return tracker == null ? damage : tracker.endStep(DamageStepTypes.BASE_COOLDOWN, damage); + } + + @Definition(id = "getEnchantedDamage", method = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F") + @Expression("? = ? * @(this.getEnchantedDamage(?, ?, ?) - ?)") + @ModifyExpressionValue(method = "attack", at = @At("MIXINEXTRAS:EXPRESSION")) + private float attack$modifyBeforeEnchantmentCooldown(final float damage) { + final SpongeAttackTracker tracker = this.attack$tracker(); + return tracker == null ? damage : tracker.startStep(DamageStepTypes.ENCHANTMENT_COOLDOWN, damage, tracker.weaponSnapshot()); + } + + @Definition(id = "getEnchantedDamage", method = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F") + @Expression("? = @(? * (this.getEnchantedDamage(?, ?, ?) - ?))") + @ModifyExpressionValue(method = "attack", at = @At("MIXINEXTRAS:EXPRESSION")) + private float attack$modifyAfterEnchantmentCooldown(final float damage) { + final SpongeAttackTracker tracker = this.attack$tracker(); + return tracker == null ? damage : tracker.endStep(DamageStepTypes.ENCHANTMENT_COOLDOWN, damage); + } + + @Inject(method = "attack", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;playServerSideSound(Lnet/minecraft/sounds/SoundEvent;)V"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;isSprinting()Z", ordinal = 0), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/Item;getAttackDamageBonus(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F"))) + private void attack$captureStrongSprint(final Entity target, final CallbackInfo ci) { + final SpongeAttackTracker tracker = this.attack$tracker(); + if (tracker != null) { + tracker.setStrongSprint(true); + } + } + + @WrapOperation(method = "attack", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/Item;getAttackDamageBonus(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) + private float attack$modifyBeforeAndAfterWeaponBonus(final Item item, final Entity target, final float originalDamage, final DamageSource source, final Operation operation) { + final SpongeAttackTracker tracker = this.attack$tracker(); + if (tracker == null) { + return operation.call(item, target, originalDamage, source); + } + + final SpongeDamageStep step = tracker.newStep(DamageStepTypes.WEAPON_BONUS, tracker.weaponSnapshot()); + float damage = (float) step.applyChildrenBefore(originalDamage); + if (!step.isSkipped()) { + damage += operation.call(item, target, damage, source); + } + return (float) step.applyChildrenAfter(damage) - originalDamage; + } + + @Expression("? = @(?) * ?") + @ModifyExpressionValue(method = "attack", at = @At("MIXINEXTRAS:EXPRESSION"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;canCriticalAttack(Lnet/minecraft/world/entity/Entity;)Z"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;isSweepAttack(ZZZ)Z") + )) + private float attack$modifyBeforeCriticalHit(final float damage) { + final SpongeAttackTracker tracker = this.attack$tracker(); + return tracker == null ? damage : tracker.startStep(DamageStepTypes.CRITICAL_HIT, damage, this); + } + + @Expression("? = @(? * ?)") + @ModifyExpressionValue(method = "attack", at = @At("MIXINEXTRAS:EXPRESSION"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;canCriticalAttack(Lnet/minecraft/world/entity/Entity;)Z"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;isSweepAttack(ZZZ)Z") + )) + private float attack$modifyAfterCriticalHit(final float damage) { + final SpongeAttackTracker tracker = this.attack$tracker(); + return tracker == null ? damage : tracker.endStep(DamageStepTypes.CRITICAL_HIT, damage); + } + + @WrapOperation(method = "attack", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurtOrSimulate(Lnet/minecraft/world/damagesource/DamageSource;F)Z"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getDeltaMovement()Lnet/minecraft/world/phys/Vec3;"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F") + )) + private boolean attack$firePostEvent(final Entity target, final DamageSource source, float damage, final Operation operation) { + final SpongeAttackTracker tracker = this.attack$tracker(); + if (tracker != null) { + final float knockbackModifier = this.shadow$getKnockback(target, source) + (tracker.isStrongSprint() ? 1.0F : 0.0F); + if (tracker.callAttackPostEvent((org.spongepowered.api.entity.Entity) target, source, damage, knockbackModifier)) { + return false; + } + damage = (float) tracker.postEvent().finalDamage(); + } + return operation.call(target, source, damage); + } + + @WrapOperation(method = "attack", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")) + private float attack$knockbackModifier(final Player self, final Entity target, final DamageSource source, final Operation operation) { + final SpongeAttackTracker tracker = this.attack$tracker(); + return tracker == null ? operation.call(self, target, source) : ((float) tracker.postEvent().knockbackModifier() - (tracker.isStrongSprint() ? 1.0F : 0.0F)); + } + + @WrapWithCondition(method = "attack", + slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;causeFoodExhaustion(F)V")), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;playServerSideSound(Lnet/minecraft/sounds/SoundEvent;)V")) + private boolean attack$preventSound(Player instance, SoundEvent soundEvent) { + final SpongeAttackTracker tracker = this.attack$tracker(); + return tracker == null || !tracker.postEvent().isCancelled(); + } + + @Inject(method = "attack", at = @At("RETURN"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F", ordinal = 0) + )) + private void attack$removeTracker(CallbackInfo ci) { + this.attack$trackers.removeLast(); + } + + @WrapOperation(method = { + "doSweepAttack(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;F)V", + // NeoForge changes where this gets invoked in a new method, so we add it as a target + "doSweepAttack(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;FLnet/minecraft/world/phys/AABB;)V" + }, + slice = @Slice(from = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;" + )), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;distanceToSqr(Lnet/minecraft/world/entity/Entity;)D"), + require = 1 + ) + private double sweepAttack$fireEvents(final Player self, final Entity sweepTarget, final Operation operation) { + final double distanceSquared = operation.call(self, sweepTarget); + if (!(distanceSquared < this.attack$interactionRangeSquared())) { + return distanceSquared; + } + + final SpongeAttackTracker mainTracker = this.attack$tracker(); + if (mainTracker == null) { + return distanceSquared; + } + + final AttackEntityEvent.Post mainEvent = mainTracker.postEvent(); + DamageSource source = (DamageSource) mainEvent.source(); + float damage = (float) (mainEvent.finalDamage() - mainTracker.damageAfter(DamageStepTypes.ENCHANTMENT_COOLDOWN)); + + final SpongeAttackTracker sweepTracker = SpongeAttackTracker.callAttackPreEvent((org.spongepowered.api.entity.Entity) sweepTarget, source, damage, mainTracker.weapon()); + if (sweepTracker == null) { + return Double.MAX_VALUE; + } + + this.attack$trackers.addLast(sweepTracker); + damage = sweepTracker.applyStartStep(); + + // In vanilla, this step is outside the loop, but we move it to here so it can be modified per target + SpongeDamageStep step = sweepTracker.newStep(DamageStepTypes.SWEEPING, sweepTracker.weaponSnapshot()); + damage = (float) step.applyChildrenBefore(damage); + if (!step.isSkipped()) { + damage = 1.0F + (float) this.shadow$getAttributeValue(Attributes.SWEEPING_DAMAGE_RATIO) * damage; + } + damage = (float) step.applyChildrenAfter(damage); + + damage = this.shadow$getEnchantedDamage(sweepTarget, damage, source); + + step = sweepTracker.newStep(DamageStepTypes.ENCHANTMENT_COOLDOWN, sweepTracker.weaponSnapshot()); + damage = (float) step.applyChildrenBefore(damage); + if (!step.isSkipped()) { + damage *= mainTracker.attackStrength(); + } + damage = (float) step.applyChildrenAfter(damage); + + if (sweepTracker.callAttackPostEvent((org.spongepowered.api.entity.Entity) sweepTarget, source, damage, 0.4F)) { + this.attack$trackers.removeLast(); + return Double.MAX_VALUE; + } + + return distanceSquared; + } + + @WrapOperation(method = { + "doSweepAttack(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;F)V", + // NeoForge changes where this gets invoked in a new method, so we add it as a target + "doSweepAttack(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;FLnet/minecraft/world/phys/AABB;)V" + }, + require = 1, + slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;")), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) + private float sweepAttack$cancelEnchantedDamage(final Player self, final Entity sweepTarget, final float damage, final DamageSource source, final Operation operation) { + return damage; // We already did it above + } + + @WrapOperation(method = { + "doSweepAttack(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;F)V", + // NeoForge changes where this gets invoked in a new method, so we add it as a target + "doSweepAttack(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;FLnet/minecraft/world/phys/AABB;)V" + }, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;knockback(DDDLnet/minecraft/world/damagesource/DamageSource;F)V"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/enchantment/EnchantmentHelper;doPostAttackEffects(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)V")), + require = 1 + ) + private void sweepAttack$knockbackModifier( + final LivingEntity sweepTarget, double modifier, final double dirX, final double dirZ, final DamageSource source, final float damage, final Operation operation) { + final SpongeAttackTracker sweepTracker = this.attack$tracker(); + if (sweepTracker != null) { + modifier = sweepTracker.postEvent().knockbackModifier(); + } + operation.call(sweepTarget, modifier, dirX, dirZ, source, damage); + } + + @WrapOperation(method = { + "doSweepAttack(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;F)V", + // NeoForge changes where this gets invoked in a new method, so we add it as a target + "doSweepAttack(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;FLnet/minecraft/world/phys/AABB;)V" + }, at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;hurtServer(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;F)Z" + ), + slice = @Slice(from = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;" + )), + require = 1 + ) + private boolean sweepAttack$finalDamage( + final LivingEntity sweepTarget, final ServerLevel level, final DamageSource source, float damage, + final Operation operation) { + final SpongeAttackTracker sweepTracker = this.attack$tracker(); + if (sweepTracker != null) { + damage = (float) sweepTracker.postEvent().finalDamage(); + } + final var result = operation.call(sweepTarget, level, source, damage); + this.attack$trackers.removeLast(); + return result; + } + + @Inject(method = "itemAttackInteraction", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;setItemInHand(Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/item/ItemStack;)V", shift = At.Shift.AFTER)) + private void attack$captureInventoryChange(final CallbackInfo ci) { + PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext().getTransactor().logPlayerInventoryChange((Player) (Object) this, PlayerInventoryTransaction.EventCreator.STANDARD); + this.inventoryMenu.broadcastChanges(); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack_Impl.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack_Impl.java deleted file mode 100644 index 25fe8979377..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack_Impl.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.entity.player; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.InventoryMenu; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.event.entity.AttackEntityEvent; -import org.spongepowered.api.event.impl.entity.AbstractModifierEvent; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Constant; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyConstant; -import org.spongepowered.asm.mixin.injection.ModifyVariable; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import org.spongepowered.common.event.tracking.PhaseContext; -import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.event.tracking.context.transaction.TransactionalCaptureSupplier; -import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; -import org.spongepowered.common.mixin.core.world.entity.LivingEntityMixin_Attack_Impl; -import org.spongepowered.common.util.DamageEventUtil; - -import java.util.ArrayList; -import java.util.Map; - -@SuppressWarnings("ConstantConditions") -@Mixin(value = Player.class, priority = 900) -public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_Impl { - - //@formatter:off - @Shadow @Final public InventoryMenu inventoryMenu; - @Shadow public abstract float shadow$getAttackStrengthScale(final float $$0); - - //@formatter:on - - private void impl$playAttackSound(Player thisPlayer, SoundEvent sound) { - if (this.bridge$vanishState().createsSounds()) { - thisPlayer.level().playSound(null, thisPlayer.getX(), thisPlayer.getY(), thisPlayer.getZ(), sound, thisPlayer.getSoundSource()); - } - } - - protected DamageEventUtil.Attack attackImpl$attack; - protected AttackEntityEvent attackImpl$attackEvent; - protected Map attackImpl$finalDamageAmounts; - - protected int attackImpl$attackStrengthTicker; - protected boolean attackImpl$isStrongSprintAttack; - - /** - * Cleanup - */ - @Inject(method = "attack", at = @At("RETURN")) - protected void attackImpl$onReturnCleanup(final Entity $$0, final CallbackInfo ci) { - this.attackImpl$attack = null; - this.attackImpl$attackEvent = null; - this.attackImpl$finalDamageAmounts = null; - } - - /** - * Captures the base damage for the {@link AttackEntityEvent} in {@link #attackImpl$attack} - * and the {@link #attackStrengthTicker} in case we need to roll it back. - * Reset {@link #attackImpl$isStrongSprintAttack} - */ - @Inject(method = "attack", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F", shift = At.Shift.BEFORE)) - protected void attackImpl$captureAttackStart(final Entity target, final CallbackInfo ci, final float baseDamage, final ItemStack weapon, final DamageSource source) { - final var strengthScale = this.shadow$getAttackStrengthScale(0.5F); - this.attackImpl$attack = new DamageEventUtil.Attack<>((Player) (Object) this, target, weapon, source, strengthScale, baseDamage, new ArrayList<>()); - this.attackImpl$attackStrengthTicker = this.attackStrengthTicker; - this.attackImpl$isStrongSprintAttack = false; - } - - /** - * Captures the enchantment damage calculations as functions - */ - @Inject(method = "attack", at = @At(value = "INVOKE", ordinal = 0, - target = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) - protected void attackImpl$enchanttDamageFunc(final Entity $$0, final CallbackInfo ci) { - // This is overridden in ServerPlayerMixin_Attack_Impl since enchantments can only be calculated on the - // server worlds. - } - - - /** - * Captures the attack-strength damage scaling as a function - */ - @Inject(method = "attack", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/player/Player;resetAttackStrengthTicker()V")) - protected void attackImpl$attackStrengthScalingDamageFunc(final Entity $$0, final CallbackInfo ci) { - // damage *= 0.2F + attackStrength * attackStrength * 0.8F; - final var strengthScaleFunc = DamageEventUtil.provideCooldownAttackStrengthFunction((Player) (Object) this, this.attackImpl$attack.strengthScale()); - this.attackImpl$attack.functions().add(strengthScaleFunc); - } - - /** - * Prevents the {@link SoundEvents#PLAYER_ATTACK_KNOCKBACK} from playing before the event. - * Captures if {@link #attackImpl$isStrongSprintAttack} for later - */ - @Redirect(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;isSprinting()Z", ordinal = 0), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/Item;getAttackDamageBonus(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V")) - protected void attackImpl$preventSprintingAttackSound(final Level instance, final Player $$0, final double $$1, final double $$2, final double $$3, final SoundEvent $$4, - final SoundSource $$5, final float $$6, final float $$7) { - // prevent sound - this.attackImpl$isStrongSprintAttack = true; - } - - /** - * Captures the weapon bonus damage as a function - */ - @Inject(method = "attack", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/item/Item;getAttackDamageBonus(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) - protected void attackImpl$attackDamageFunc(final Entity $$0, final CallbackInfo ci) { - // damage += weaponItem.getItem().getAttackDamageBonus(targetEntity, damage, damageSource); - final var bonusDamageFunc = DamageEventUtil.provideWeaponAttackDamageBonusFunction(this.attackImpl$attack.target(), this.attackImpl$attack.weapon(), this.attackImpl$attack.dmgSource()); - this.attackImpl$attack.functions().add(bonusDamageFunc); - } - - /** - * Capture damageSource for sweep attacks event later - * Calculate knockback earlier than vanilla for event - * call the AttackEntityEvent - * Play prevented sound from {@link #attackImpl$preventSprintingAttackSound} - * returns false if canceled, appearing for vanilla as an invulnerable target. {@link #attackImpl$onNoDamageSound} - */ - @WrapOperation(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getDeltaMovement()Lnet/minecraft/world/phys/Vec3;"), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")) - protected boolean attackImpl$onHurt(final Entity targetEntity, final DamageSource damageSource, final float mcDamage, final Operation original) { - float knockbackModifier = this.shadow$getKnockback(targetEntity, damageSource) + (this.attackImpl$isStrongSprintAttack ? 1.0F : 0.0F); - this.attackImpl$attackEvent = DamageEventUtil.callPlayerAttackEntityEvent(this.attackImpl$attack, knockbackModifier); - - if (this.attackImpl$attackEvent.isCancelled()) { - // TODO this is actually not really doing anything because a ServerboundSwingPacket also resets it immediatly after - this.attackStrengthTicker = this.attackImpl$attackStrengthTicker; // Reset to old value - return false; - } - - this.attackImpl$finalDamageAmounts = AbstractModifierEvent.finalAmounts(this.attackImpl$attackEvent.originalDamage(), this.attackImpl$attackEvent.modifiers()); - - if (this.attackImpl$isStrongSprintAttack) { - // Play prevented sprint attack sound - this.impl$playAttackSound((Player) (Object) this, SoundEvents.PLAYER_ATTACK_KNOCKBACK); - } - - return original.call(targetEntity, damageSource, (float) this.attackImpl$attackEvent.finalOutputDamage()); - } - - /** - * Set enchantment damage with value from event - */ - @ModifyVariable(method = "attack", ordinal = 1, - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z"), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;knockback(DDD)V", ordinal = 0)), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")) - protected float attackImpl$enchentmentDamageFromEvent(final float enchDmg) { - return this.attackImpl$finalDamageAmounts.getOrDefault(ResourceKey.minecraft("attack_enchantment"), 0.0).floatValue(); - } - - /** - * Redirects Player#getKnockback to the attack event value - */ - @Redirect(method = "attack", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")) - protected float attackImpl$sweepHook(final Player instance, final Entity entity, final DamageSource damageSource) { - return this.attackImpl$attackEvent.knockbackModifier(); - } - - @ModifyConstant(method = "attack", constant = @Constant(floatValue = 1.0F), slice = @Slice( - from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F"), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;knockback(DDD)V")) - ) - private float attackImpl$noDoubleAddKb(final float constant) { - return 0.0F; - } - - /** - * Prevents the {@link SoundEvents#PLAYER_ATTACK_NODAMAGE} when event was canceled - */ - @Redirect(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;causeFoodExhaustion(F)V")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V")) - protected void attackImpl$onNoDamageSound(final Level instance, final Player $$0, final double $$1, final double $$2, final double $$3, - final SoundEvent $$4, final SoundSource $$5, final float $$6, final float $$7) { - if (!this.attackImpl$attackEvent.isCancelled()) { - this.impl$playAttackSound((Player) (Object) this, SoundEvents.PLAYER_ATTACK_NODAMAGE); - } - } - - /** - * Call Sweep Attack Events - */ - @WrapOperation(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;distanceToSqr(Lnet/minecraft/world/entity/Entity;)D")) - protected double attackImpl$beforeSweepHurt(final Player instance, final Entity entity, final Operation original) { - return original.call(instance, entity); - } - - /** - * Redirect Player#getEnchantedDamage to sweep event value - */ - @Redirect(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) - protected float attackImpl$beforeSweepHurt(final Player instance, final Entity $$0, final float $$1, final DamageSource $$2) { - return (float) this.attackImpl$attackEvent.finalOutputDamage(); - } - - /** - * Redirect {@link LivingEntity#knockback} to use modified event knockback - */ - @WrapOperation(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;"), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;knockback(DDD)V")) - protected void attackImpl$modifyKnockback(final LivingEntity instance, final double strength, final double x, final double z, final Operation original) { - original.call(instance, strength * this.attackImpl$attackEvent.knockbackModifier(), x, z); - } - - /** - * Captures inventory changes - */ - @Inject(method = "attack", at = @At(value = "INVOKE", shift = At.Shift.AFTER, - target = "Lnet/minecraft/world/entity/player/Player;setItemInHand(Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/item/ItemStack;)V")) - protected void attackImpl$causeInventoryCapture(final CallbackInfo ci) { - // Capture... - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); - final TransactionalCaptureSupplier transactor = context.getTransactor(); - transactor.logPlayerInventoryChange((Player) (Object) this, PlayerInventoryTransaction.EventCreator.STANDARD); - this.inventoryMenu.broadcastChanges(); - } - - @Inject(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getDamageAfterArmorAbsorb(Lnet/minecraft/world/damagesource/DamageSource;F)F")) - protected void attackImpl$startActuallyHurt(DamageSource damageSource, float originalDamage, CallbackInfo ci) { - // TODO check for direct call? - this.attackImpl$actuallyHurt = new DamageEventUtil.ActuallyHurt((LivingEntity) (Object) this, new ArrayList<>(), damageSource, originalDamage); - } - - /** - * Set final damage after calling {@link Player#setAbsorptionAmount} in which we called the event - * !!NOTE that var7 is actually decompiled incorrectly!! - * It is NOT the final damage value instead the method parameter is mutated - */ - @ModifyVariable(method = "actuallyHurt", ordinal = 0, - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;setAbsorptionAmount(F)V", - shift = At.Shift.AFTER), argsOnly = true) - protected float attackImpl$setFinalDamage(final float value) { - if (this.attackImpl$actuallyHurtResult.event().isCancelled()) { - return 0; - } - return this.attackImpl$actuallyHurtFinalDamage; - } - - /** - * Cleanup - */ - @Inject(method = "actuallyHurt", at = @At("RETURN")) - protected void attackImpl$afterActuallyHurt(final DamageSource $$0, final float $$1, final CallbackInfo ci) { - this.attackImpl$handlePostDamage(); - this.attackImpl$actuallyHurt = null; - this.attackImpl$actuallyHurtResult = null; - } - - -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Shared_Damage.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Shared_Damage.java new file mode 100644 index 00000000000..4d5b7f1dd1e --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Shared_Damage.java @@ -0,0 +1,63 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity.player; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import net.minecraft.resources.Identifier; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; +import org.spongepowered.common.mixin.core.world.entity.LivingEntityMixin_Damage; + +// Forge and Vanilla +@Mixin(value = Player.class, priority = 900) +public abstract class PlayerMixin_Shared_Damage extends LivingEntityMixin_Damage { + + @ModifyVariable(method = "actuallyHurt", at = @At("STORE"), argsOnly = true, slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getDamageAfterMagicAbsorb(Lnet/minecraft/world/damagesource/DamageSource;F)F"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getAbsorptionAmount()F", ordinal = 0))) + private float damage$modifyBeforeAbsorption(final float damage) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null ? damage : tracker.startStep(DamageStepTypes.ABSORPTION, damage, this); + } + + @WrapWithCondition(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;setAbsorptionAmount(F)V")) + private boolean damage$skipAbsorption(final Player self, final float absorption) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null || !tracker.isSkipped(DamageStepTypes.ABSORPTION); + } + + @WrapWithCondition(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;awardStat(Lnet/minecraft/resources/Identifier;I)V"), slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;setAbsorptionAmount(F)V"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;causeFoodExhaustion(F)V"))) + private boolean damage$skipAbsorptionStat(final Player self, final Identifier stat, final int amount) { + final SpongeDamageTracker tracker = this.damage$tracker(); + return tracker == null || !tracker.isSkipped(DamageStepTypes.ABSORPTION); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/EyeOfEnderMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/EyeOfEnderMixin.java index bf3c3ba5950..214776a127a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/EyeOfEnderMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/EyeOfEnderMixin.java @@ -25,6 +25,7 @@ package org.spongepowered.common.mixin.core.world.entity.projectile; import net.minecraft.world.entity.projectile.EyeOfEnder; +import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -32,27 +33,24 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.bridge.LocationTargetingBridge; import org.spongepowered.common.mixin.core.world.entity.EntityMixin; +import org.spongepowered.common.util.VecHelper; import org.spongepowered.math.vector.Vector3d; @Mixin(EyeOfEnder.class) public abstract class EyeOfEnderMixin extends EntityMixin implements LocationTargetingBridge { // @formatter:off - @Shadow private double tx; - @Shadow private double ty; - @Shadow private double tz; + @Shadow private Vec3 target; // @formatter:on @Override public Vector3d bridge$getTargetedPosition() { - return new Vector3d(this.tx, this.ty, this.tz); + return VecHelper.toVector3d(this.target); } @Override public void bridge$setTargetedPosition(final Vector3d vec) { - this.tx = vec.x(); - this.ty = vec.y(); - this.tz = vec.z(); + this.target = VecHelper.toVanillaVector3d(vec); } @Inject( diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/FireworkRocketEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/FireworkRocketEntityMixin.java index 9f9b9d4b8cb..b88791d9f7d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/FireworkRocketEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/FireworkRocketEntityMixin.java @@ -24,13 +24,15 @@ */ package org.spongepowered.common.mixin.core.world.entity.projectile; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.FireworkRocketEntity; -import net.minecraft.world.level.Level; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.Sponge; import org.spongepowered.api.entity.projectile.explosive.FireworkRocket; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.world.explosion.Explosion; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -40,7 +42,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.bridge.explosives.ExplosiveBridge; import org.spongepowered.common.bridge.explosives.FusedExplosiveBridge; -import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.util.Constants; @@ -54,7 +55,7 @@ public abstract class FireworkRocketEntityMixin extends ProjectileMixin implemen @Shadow private int lifetime; // @formatter:on - private int impl$explosionRadius = Constants.Entity.Firework.DEFAULT_EXPLOSION_RADIUS; + private float impl$explosionRadius = Constants.Entity.Firework.DEFAULT_EXPLOSION_RADIUS; @Override public boolean bridge$isPrimed() { @@ -83,41 +84,43 @@ public abstract class FireworkRocketEntityMixin extends ProjectileMixin implemen } @Override - public Optional bridge$getExplosionRadius() { + public Optional bridge$getExplosionRadius() { return Optional.of(this.impl$explosionRadius); } @Override - public void bridge$setExplosionRadius(final @Nullable Integer radius) { + public void bridge$setExplosionRadius(final @Nullable Float radius) { this.impl$explosionRadius = radius == null ? Constants.Entity.Firework.DEFAULT_EXPLOSION_RADIUS : radius; } - @Redirect(method = "explode()V", + @Redirect(method = "explode(Lnet/minecraft/server/level/ServerLevel;)V", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;broadcastEntityEvent(Lnet/minecraft/world/entity/Entity;B)V") + target = "Lnet/minecraft/server/level/ServerLevel;broadcastEntityEvent(Lnet/minecraft/world/entity/Entity;B)V") ) - private void impl$useSpongeExplosion(final Level world, final Entity self, final byte state) { - if (this.shadow$level().isClientSide) { - world.broadcastEntityEvent(self, state); - return; - } + private void impl$useSpongeExplosion(final ServerLevel world, final Entity self, final byte state) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { // Fireworks don't typically explode like other explosives, but we'll // post an event regardless and if the radius is zero the explosion // won't be triggered (the default behavior). frame.pushCause(this); frame.addContext(EventContextKeys.PROJECTILE_SOURCE, this.impl$getProjectileSource()); - SpongeCommonEventFactory.detonateExplosive(this, Explosion.builder() + final var explosionBuilder = Explosion.builder() .sourceExplosive(((FireworkRocket) this)) .location(((FireworkRocket) this).serverLocation()) - .radius(this.impl$explosionRadius)) - .ifPresent(explosion -> world.broadcastEntityEvent(self, state)); + .radius(this.impl$explosionRadius); + + final var detonateEvent = SpongeEventFactory.createDetonateExplosiveEvent(PhaseTracker.getInstance().currentCause(), + explosionBuilder, (FireworkRocket) this, explosionBuilder.build()); + if (Sponge.eventManager().post(detonateEvent)) { + return; + } + world.broadcastEntityEvent(self, state); } } @Inject(method = "tick()V", at = @At("RETURN")) private void impl$postPrimeEvent(final CallbackInfo ci) { - if (this.life == 1 && !this.shadow$level().isClientSide) { + if (this.life == 1 && !this.shadow$level().isClientSide()) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(this); frame.addContext(EventContextKeys.PROJECTILE_SOURCE, this.impl$getProjectileSource()); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/FishingHookMixin_Shared.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/FishingHookMixin_Shared.java index 7ae55d5b57e..9f19f6ca213 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/FishingHookMixin_Shared.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/FishingHookMixin_Shared.java @@ -47,6 +47,6 @@ public abstract class FishingHookMixin_Shared extends ProjectileMixin { this.shadow$discard(); return ProjectileDeflection.NONE; } - return this.hitTargetOrDeflectSelf(hitResult); + return this.shadow$hitTargetOrDeflectSelf(hitResult); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ProjectileMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ProjectileMixin.java index d46a1507734..98072781d2f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ProjectileMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ProjectileMixin.java @@ -25,6 +25,7 @@ package org.spongepowered.common.mixin.core.world.entity.projectile; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityReference; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.entity.projectile.ProjectileDeflection; import net.minecraft.world.phys.HitResult; @@ -48,14 +49,13 @@ public abstract class ProjectileMixin extends EntityMixin { // @formatter:off - @Shadow protected abstract void shadow$onHit(HitResult result); - @Shadow public abstract void shadow$setOwner(@Nullable Entity p_212361_1_); @Shadow public abstract @Nullable Entity shadow$getOwner(); @Shadow protected abstract ProjectileDeflection shadow$hitTargetOrDeflectSelf(HitResult result); - @Shadow protected abstract void onHit(HitResult result); - @Shadow protected abstract ProjectileDeflection hitTargetOrDeflectSelf(HitResult result); + @Shadow protected abstract void shadow$onHit(HitResult $$0); + @Shadow @Nullable protected EntityReference owner; // @formatter:on + private ProjectileSource impl$projectileSource = UnknownProjectileSource.UNKNOWN; protected ProjectileSource impl$getProjectileSource() { @@ -69,7 +69,7 @@ public abstract class ProjectileMixin extends EntityMixin { return this.impl$projectileSource; } - @Inject(method = "setOwner", at = @At("RETURN")) + @Inject(method = "setOwner(Lnet/minecraft/world/entity/Entity;)V", at = @At("RETURN")) private void impl$assignProjectileSource(final @Nullable Entity owner, final CallbackInfo ci) { this.impl$projectileSource = owner == null ? UnknownProjectileSource.UNKNOWN : (EntityProjectileSource) owner; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ProjectileUtilMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ProjectileUtilMixin.java index fefa12138c1..de5d48ad2d2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ProjectileUtilMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ProjectileUtilMixin.java @@ -26,6 +26,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.ProjectileUtil; import net.minecraft.world.level.Level; @@ -70,21 +71,21 @@ public abstract class ProjectileUtilMixin { final Vec3 to = from.add(velocity); final Vec3 direction = from.subtract(to); - cir.setReturnValue(BlockHitResult.miss(to, Direction.getNearest(direction.x, direction.y, direction.z), BlockPos.containing(to))); + cir.setReturnValue(BlockHitResult.miss(to, Direction.getApproximateNearest(direction.x, direction.y, direction.z), BlockPos.containing(to))); } } } @Redirect(method = { "getHitResult(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/entity/Entity;Ljava/util/function/Predicate;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/level/Level;FLnet/minecraft/world/level/ClipContext$Block;)Lnet/minecraft/world/phys/HitResult;", - "getEntityHitResult(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Lnet/minecraft/world/phys/EntityHitResult;" + "getEntityHitResult(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/projectile/Projectile;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Lnet/minecraft/world/phys/EntityHitResult;" }, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/ProjectileUtil;getEntityHitResult(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;F)Lnet/minecraft/world/phys/EntityHitResult;")) private static EntityHitResult impl$onGetEntityHitResult(final Level $$0, final Entity $$1, final Vec3 $$2, final Vec3 $$3, final AABB $$4, final Predicate $$5, final float $$6) { - if ($$0.isClientSide) { + if ($$0.isClientSide()) { return ProjectileUtil.getEntityHitResult($$0, $$1, $$2, $$3, $$4, $$5, $$6); } try (final PhaseContext<@NonNull ?> context = EntityPhase.State.COLLISION - .createPhaseContext(PhaseTracker.SERVER) + .createPhaseContext(PhaseTracker.getWorldInstance((ServerLevel) $$0)) .source($$1) ) { context.buildAndSwitch(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ShulkerBulletMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ShulkerBulletMixin.java index dc90ee08343..79649ea2e18 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ShulkerBulletMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ShulkerBulletMixin.java @@ -24,10 +24,11 @@ */ package org.spongepowered.common.mixin.core.world.entity.projectile; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.ShulkerBullet; import net.minecraft.world.phys.HitResult; +import org.spongepowered.api.entity.Entity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -35,7 +36,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.event.SpongeCommonEventFactory; -import org.spongepowered.common.util.DamageEventUtil; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; @Mixin(ShulkerBullet.class) public abstract class ShulkerBulletMixin extends ProjectileMixin { @@ -50,10 +51,10 @@ private void onBulletHitBlock(final HitResult result, final CallbackInfo ci) { } } - @Inject(method = "hurt", cancellable = true, at = @At(value = "INVOKE", + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/ShulkerBullet;playSound(Lnet/minecraft/sounds/SoundEvent;FF)V")) - private void attackImpl$onAttackEntityFrom(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { - if (DamageEventUtil.callOtherAttackEvent((Entity) (Object) this, source, amount).isCancelled()) { + private void attack$onHurt(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { + if (SpongeDamageTracker.callDamageEvents((Entity) this, source, damage) == null) { cir.setReturnValue(true); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/AbstractArrowMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/arrow/AbstractArrowMixin.java similarity index 88% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/AbstractArrowMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/arrow/AbstractArrowMixin.java index 6f91c80b19a..72564fc5f55 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/AbstractArrowMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/arrow/AbstractArrowMixin.java @@ -22,12 +22,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.projectile; +package org.spongepowered.common.mixin.core.world.entity.projectile.arrow; +import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.entity.projectile.arrow.AbstractArrow; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; @@ -44,24 +45,25 @@ import org.spongepowered.common.bridge.world.entity.projectile.AbstractArrowBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.event.SpongeCommonEventFactory; +import org.spongepowered.common.mixin.core.world.entity.projectile.ProjectileMixin; @Mixin(AbstractArrow.class) public abstract class AbstractArrowMixin extends ProjectileMixin implements AbstractArrowBridge { // @formatter:off @Shadow private int life; - @Shadow protected boolean inGround; @Shadow public int shakeTime; @Shadow public AbstractArrow.Pickup pickup; @Shadow @Nullable private BlockState lastState; @Shadow public abstract void shadow$setCritArrow(boolean critical); - @Shadow public abstract void shadow$setPierceLevel(byte level); + @Shadow protected abstract void shadow$setPierceLevel(byte level); @Shadow protected abstract ItemStack shadow$getPickupItem(); @Shadow protected abstract void resetPiercedEntities(); - + @Shadow protected abstract void shadow$setInGround(boolean $$0); // @formatter:on + @Nullable private Double impl$customKnockback; // Not all ProjectileSources are entities (e.g. BlockProjectileSource). @@ -91,14 +93,12 @@ private void onProjectileHit(final BlockHitResult hitResult, final CallbackInfo this.impl$getProjectileSource(), hitResult)) { this.shadow$playSound(SoundEvents.ARROW_HIT, 1.0F, 1.2F / (this.random.nextFloat() * 0.2F + 0.9F)); // Make it almost look like it collided with something - final BlockHitResult blockraytraceresult = (BlockHitResult)hitResult; - final BlockState blockstate = this.shadow$level().getBlockState(blockraytraceresult.getBlockPos()); - this.lastState = blockstate; - final Vec3 vec3d = blockraytraceresult.getLocation().subtract(this.shadow$getX(), this.shadow$getY(), this.shadow$getZ()); + this.lastState = this.shadow$level().getBlockState(hitResult.getBlockPos()); + final Vec3 vec3d = hitResult.getLocation().subtract(this.shadow$getX(), this.shadow$getY(), this.shadow$getZ()); this.shadow$setDeltaMovement(vec3d); final Vec3 vec3d1 = vec3d.normalize().scale(0.05F); this.shadow$setPos(this.shadow$getX() - vec3d1.x, this.shadow$getY() - vec3d1.y, this.shadow$getZ() - vec3d1.z); - this.inGround = true; + this.shadow$setInGround(true); this.shakeTime = 7; this.shadow$setCritArrow(false); this.shadow$setPierceLevel((byte)0); @@ -136,9 +136,9 @@ private void onProjectileHit(final EntityHitResult hitResult, final CallbackInfo this.shadow$setYRot(this.shadow$getYRot() + 180.0F); this.yRotO += 180.0F; this.life = 0; - if (!this.shadow$level().isClientSide && this.shadow$getDeltaMovement().lengthSqr() < 1.0E-7D) { + if (!this.shadow$level().isClientSide() && this.shadow$getDeltaMovement().lengthSqr() < 1.0E-7D) { if (this.pickup == AbstractArrow.Pickup.ALLOWED) { - this.shadow$spawnAtLocation(this.shadow$getPickupItem(), 0.1F); + this.shadow$spawnAtLocation((ServerLevel) this.shadow$level(), this.shadow$getPickupItem(), 0.1F); } this.shadow$discard(); @@ -150,7 +150,7 @@ private void onProjectileHit(final EntityHitResult hitResult, final CallbackInfo @Inject( method = "tickDespawn", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/AbstractArrow;discard()V") + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/arrow/AbstractArrow;discard()V") ) private void impl$throwExpireArrow(final CallbackInfo ci) { this.impl$callExpireEntityEvent(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ThrownTridentMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/arrow/ThrownTridentMixin.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ThrownTridentMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/arrow/ThrownTridentMixin.java index d5abc0c2954..c5b4589a0c3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ThrownTridentMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/arrow/ThrownTridentMixin.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.projectile; +package org.spongepowered.common.mixin.core.world.entity.projectile.arrow; import net.minecraft.sounds.SoundEvents; -import net.minecraft.world.entity.projectile.ThrownTrident; +import net.minecraft.world.entity.projectile.arrow.ThrownTrident; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.HitResult; import org.spongepowered.asm.mixin.Mixin; @@ -35,6 +35,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.event.SpongeCommonEventFactory; +import org.spongepowered.common.mixin.core.world.entity.projectile.ProjectileMixin; @Mixin(ThrownTrident.class) public abstract class ThrownTridentMixin extends ProjectileMixin { diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/AbstractHurtingProjectileMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectileMixin.java similarity index 86% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/AbstractHurtingProjectileMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectileMixin.java index b909d0e1e36..d1354b772d2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/AbstractHurtingProjectileMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectileMixin.java @@ -22,22 +22,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.projectile; +package org.spongepowered.common.mixin.core.world.entity.projectile.hurtingprojectile; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.projectile.AbstractHurtingProjectile; import net.minecraft.world.entity.projectile.ProjectileDeflection; +import net.minecraft.world.entity.projectile.hurtingprojectile.AbstractHurtingProjectile; import net.minecraft.world.phys.HitResult; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.event.SpongeCommonEventFactory; +import org.spongepowered.common.mixin.core.world.entity.projectile.ProjectileMixin; @Mixin(AbstractHurtingProjectile.class) public abstract class AbstractHurtingProjectileMixin extends ProjectileMixin { - @Redirect(method = "tick()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/AbstractHurtingProjectile;hitTargetOrDeflectSelf(Lnet/minecraft/world/phys/HitResult;)Lnet/minecraft/world/entity/projectile/ProjectileDeflection;")) + @Redirect(method = "tick()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectile;hitTargetOrDeflectSelf(Lnet/minecraft/world/phys/HitResult;)Lnet/minecraft/world/entity/projectile/ProjectileDeflection;")) private ProjectileDeflection impl$callCollideImpactEvent(AbstractHurtingProjectile projectile, HitResult result) { if (result.getType() == HitResult.Type.MISS || ((LevelBridge) this.shadow$level()).bridge$isFake()) { return this.shadow$hitTargetOrDeflectSelf(result); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/LargeFireballMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/LargeFireballMixin.java similarity index 54% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/LargeFireballMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/LargeFireballMixin.java index 2cf1ee1785e..0ea2b682b46 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/LargeFireballMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/LargeFireballMixin.java @@ -22,28 +22,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.projectile; +package org.spongepowered.common.mixin.core.world.entity.projectile.hurtingprojectile; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.projectile.LargeFireball; +import net.minecraft.world.entity.projectile.hurtingprojectile.LargeFireball; import net.minecraft.world.level.Level; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.projectile.Projectile; -import org.spongepowered.api.entity.projectile.explosive.fireball.ExplosiveFireball; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; -import org.spongepowered.api.world.explosion.Explosion; -import org.spongepowered.api.world.server.ServerLocation; -import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.bridge.explosives.ExplosiveBridge; -import org.spongepowered.common.bridge.world.entity.GrieferBridge; import org.spongepowered.common.bridge.world.entity.projectile.LargeFireballBridge; -import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.util.Constants; @@ -53,54 +47,38 @@ public abstract class LargeFireballMixin extends AbstractHurtingProjectileMixin implements LargeFireballBridge, ExplosiveBridge { // @formatter:off - @Shadow public int explosionPower; + @Shadow private int explosionPower; // @formatter:on - /** - * @author gabizou April 13th, 2018 - * @reason Due to changes from Forge, we have to redirect osr modify the gamerule check, - * but since forge doesn't allow us to continue to check the gamerule method call here, - * we have to modify the arguments passed in (the two booleans). There may be a better way, - * which may include redirecting the world.newExplosion method call instead of modifyargs, - * but, it is what it is. - */ @Redirect(method = "onHit", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;explode(Lnet/minecraft/world/entity/Entity;DDDFZLnet/minecraft/world/level/Level$ExplosionInteraction;)Lnet/minecraft/world/level/Explosion;" + target = "Lnet/minecraft/world/level/Level;explode(Lnet/minecraft/world/entity/Entity;DDDFZLnet/minecraft/world/level/Level$ExplosionInteraction;)V" ) ) - public net.minecraft.world.level.@Nullable Explosion impl$throwExplosionEventAndExplode(final net.minecraft.world.level.Level worldObj, @Nullable final Entity nil, - final double x, final double y, final double z, final float strength, final boolean flaming, final Level.ExplosionInteraction mode) { - return this.bridge$throwExplosionEventAndExplode(worldObj, nil, x, y, z, strength, flaming, mode); + public void impl$onHitExplode(final Level world, final Entity thisEntity, + final double x, final double y, final double z, final float explosionRadius, final boolean causesFire, + final Level.ExplosionInteraction mode) { + this.bridge$wrappedExplode(x, y, z, explosionRadius, causesFire, mode); } @Override - public net.minecraft.world.level.Explosion bridge$throwExplosionEventAndExplode(net.minecraft.world.level.Level worldObj, @Nullable Entity nil, - double x, double y, double z, float strength, boolean flaming, Level.ExplosionInteraction mode) { - final boolean griefer = ((GrieferBridge) this).bridge$canGrief(); + public void bridge$wrappedExplode(final double x, final double y, final double z, + final float explosionRadius, final boolean causesFire, final Level.ExplosionInteraction mode) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(this); ((Projectile) this).get(Keys.SHOOTER).ifPresent(shooter -> frame.addContext(EventContextKeys.PROJECTILE_SOURCE, shooter)); - final Optional ex = SpongeCommonEventFactory.detonateExplosive(this, Explosion.builder() - .location(ServerLocation.of((ServerWorld) worldObj, x, y, z)) - .sourceExplosive(((ExplosiveFireball) this)) - .radius(strength) - .canCauseFire(flaming && griefer) - .shouldPlaySmoke(mode != Level.ExplosionInteraction.NONE && griefer) - .shouldBreakBlocks(mode != Level.ExplosionInteraction.NONE && griefer)); - - return ex.orElse(null); + this.level().explode((LargeFireball) (Object) this, x, y, z, explosionRadius, causesFire, mode); } } @Override - public Optional bridge$getExplosionRadius() { - return Optional.of(this.explosionPower); + public Optional bridge$getExplosionRadius() { + return Optional.of((float) this.explosionPower); } @Override - public void bridge$setExplosionRadius(@Nullable final Integer radius) { - this.explosionPower = radius == null ? Constants.Entity.Fireball.DEFAULT_EXPLOSION_RADIUS : radius; + public void bridge$setExplosionRadius(final @Nullable Float radius) { + this.explosionPower = radius == null ? Constants.Entity.Fireball.DEFAULT_EXPLOSION_RADIUS : radius.intValue(); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/SmallFireballMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/SmallFireballMixin.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/SmallFireballMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/SmallFireballMixin.java index c63e625c155..2c5a7ac1cde 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/SmallFireballMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/SmallFireballMixin.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.projectile; +package org.spongepowered.common.mixin.core.world.entity.projectile.hurtingprojectile; import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.projectile.SmallFireball; +import net.minecraft.world.entity.projectile.hurtingprojectile.SmallFireball; import net.minecraft.world.level.Level; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/WitherSkullMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/WitherSkullMixin.java similarity index 62% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/WitherSkullMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/WitherSkullMixin.java index 08c67dff946..b65f0b5b3a7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/WitherSkullMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/hurtingprojectile/WitherSkullMixin.java @@ -22,49 +22,43 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.projectile; +package org.spongepowered.common.mixin.core.world.entity.projectile.hurtingprojectile; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.projectile.hurtingprojectile.WitherSkull; import net.minecraft.world.level.Level; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.projectile.Projectile; -import org.spongepowered.api.entity.projectile.explosive.WitherSkull; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; -import org.spongepowered.api.world.explosion.Explosion; -import org.spongepowered.api.world.server.ServerLocation; -import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.accessor.world.entity.projectile.ProjectileAccessor; import org.spongepowered.common.bridge.explosives.ExplosiveBridge; -import org.spongepowered.common.bridge.world.entity.GrieferBridge; import org.spongepowered.common.bridge.world.entity.projectile.WitherSkullBridge; -import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.util.Constants; import java.util.Optional; -@Mixin(net.minecraft.world.entity.projectile.WitherSkull.class) +@Mixin(net.minecraft.world.entity.projectile.hurtingprojectile.WitherSkull.class) public abstract class WitherSkullMixin extends AbstractHurtingProjectileMixin implements WitherSkullBridge, ExplosiveBridge { - private int impl$explosionRadius = Constants.Entity.WitherSkull.DEFAULT_EXPLOSION_RADIUS; + private float impl$explosionRadius = Constants.Entity.WitherSkull.DEFAULT_EXPLOSION_RADIUS; // TODO Key not implemented private float impl$damage = 0.0f; private boolean impl$damageSet = false; @ModifyArg(method = "onHitEntity", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")) + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurtServer(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;F)Z")) private float impl$onAttackEntityFrom(final float amount) { if (this.impl$damageSet) { return this.impl$damage; } - if (((ProjectileAccessor) this).accessor$ownerUUID() != null) { + if (this.owner != null) { return Constants.Entity.WitherSkull.DEFAULT_WITHER_CREATED_SKULL_DAMAGE; } return Constants.Entity.WitherSkull.DEFAULT_NO_SOURCE_SKULL_DAMAGE; @@ -72,41 +66,31 @@ public abstract class WitherSkullMixin extends AbstractHurtingProjectileMixin im // Explosive Impl @Override - public Optional bridge$getExplosionRadius() { + public Optional bridge$getExplosionRadius() { return Optional.of(this.impl$explosionRadius); } @Override - public void bridge$setExplosionRadius(final @Nullable Integer explosionRadius) { + public void bridge$setExplosionRadius(final @Nullable Float explosionRadius) { this.impl$explosionRadius = explosionRadius == null ? Constants.Entity.WitherSkull.DEFAULT_EXPLOSION_RADIUS : explosionRadius; } @Redirect(method = "onHit", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;explode(Lnet/minecraft/world/entity/Entity;DDDFZLnet/minecraft/world/level/Level$ExplosionInteraction;)Lnet/minecraft/world/level/Explosion;")) - public net.minecraft.world.level.@Nullable Explosion impl$CreateAndProcessExplosionEvent(final net.minecraft.world.level.Level worldObj, final Entity self, - final double x, final double y, final double z, final float strength, final boolean flaming, final Level.ExplosionInteraction mode) { - return this.bridge$throwExplosionEventAndExplosde(worldObj, self, x, y, z, strength, flaming, mode); + target = "Lnet/minecraft/world/level/Level;explode(Lnet/minecraft/world/entity/Entity;DDDFZLnet/minecraft/world/level/Level$ExplosionInteraction;)V")) + public void impl$onHitExplode(final net.minecraft.world.level.Level worldObj, final Entity self, + final double x, final double y, final double z, final float explosionRadius, final boolean causesFire, final Level.ExplosionInteraction mode) { + this.bridge$wrappedExplode(x, y, z, explosionRadius, causesFire, mode); } @Override - public net.minecraft.world.level.Explosion bridge$throwExplosionEventAndExplosde( - final net.minecraft.world.level.Level worldObj, final Entity self, - final double x, final double y, final double z, final float strength, final boolean flaming, final Level.ExplosionInteraction mode) { - final boolean griefer = ((GrieferBridge) this).bridge$canGrief(); + public void bridge$wrappedExplode(final double x, final double y, final double z, final float explosionRadius, final boolean causesFire, final Level.ExplosionInteraction mode) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(this); ((Projectile) this).get(Keys.SHOOTER).ifPresent(shooter -> { frame.addContext(EventContextKeys.PROJECTILE_SOURCE, shooter); frame.pushCause(shooter); }); - return SpongeCommonEventFactory.detonateExplosive(this, Explosion.builder() - .location(ServerLocation.of((ServerWorld) worldObj, x, y, z)) - .sourceExplosive(((WitherSkull) this)) - .radius(this.impl$explosionRadius) - .canCauseFire(flaming) - .shouldPlaySmoke(mode != Level.ExplosionInteraction.NONE && griefer) - .shouldBreakBlocks(mode != Level.ExplosionInteraction.NONE && griefer)) - .orElse(null); + this.level().explode((WitherSkull) (Object) this, x, y, z, explosionRadius, causesFire, mode); } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/SnowballMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/throwableitemprojectile/SnowballMixin.java similarity index 88% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/SnowballMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/throwableitemprojectile/SnowballMixin.java index 5286cd530d4..b07e81e60cc 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/SnowballMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/throwableitemprojectile/SnowballMixin.java @@ -22,14 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.projectile; +package org.spongepowered.common.mixin.core.world.entity.projectile.throwableitemprojectile; import org.spongepowered.api.entity.projectile.Snowball; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.common.mixin.core.world.entity.projectile.ThrowableProjectileMixin; -@Mixin(net.minecraft.world.entity.projectile.Snowball.class) +@Mixin(net.minecraft.world.entity.projectile.throwableitemprojectile.Snowball.class) public abstract class SnowballMixin extends ThrowableProjectileMixin implements Snowball { // TODO Key not implemented @@ -37,7 +38,7 @@ public abstract class SnowballMixin extends ThrowableProjectileMixin implements private boolean impl$damageSet = false; @ModifyArg(method = "onHitEntity", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")) + target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)V")) private float impl$onAttackEntityFromUseCustomDamage(float damage) { return this.impl$damageSet ? (float) this.impl$damageAmount : damage; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ThrownEggMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/throwableitemprojectile/ThrownEggMixin.java similarity index 88% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ThrownEggMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/throwableitemprojectile/ThrownEggMixin.java index e94c66d2615..592656ff2b4 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ThrownEggMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/throwableitemprojectile/ThrownEggMixin.java @@ -22,12 +22,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.projectile; +package org.spongepowered.common.mixin.core.world.entity.projectile.throwableitemprojectile; -import net.minecraft.world.entity.projectile.ThrownEgg; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownEgg; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.common.mixin.core.world.entity.projectile.ThrowableProjectileMixin; @Mixin(ThrownEgg.class) public abstract class ThrownEggMixin extends ThrowableProjectileMixin { @@ -37,7 +38,7 @@ public abstract class ThrownEggMixin extends ThrowableProjectileMixin { @ModifyArg(method = "onHitEntity", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z") + target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)V") ) private float onAttackEntityFromUseDamage(final float damage) { return (float) this.impl$damageAmount; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ThrownEnderpearlMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/throwableitemprojectile/ThrownEnderpearlMixin.java similarity index 78% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ThrownEnderpearlMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/throwableitemprojectile/ThrownEnderpearlMixin.java index fde7cbcc0ce..07b38137601 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/ThrownEnderpearlMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/projectile/throwableitemprojectile/ThrownEnderpearlMixin.java @@ -22,11 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.projectile; +package org.spongepowered.common.mixin.core.world.entity.projectile.throwableitemprojectile; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.projectile.ThrownEnderpearl; -import net.minecraft.world.level.portal.DimensionTransition; +import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownEnderpearl; +import net.minecraft.world.level.portal.TeleportTransition; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.cause.entity.MovementTypes; @@ -35,6 +35,7 @@ import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.mixin.core.world.entity.projectile.ThrowableProjectileMixin; @Mixin(ThrownEnderpearl.class) public abstract class ThrownEnderpearlMixin extends ThrowableProjectileMixin { @@ -44,21 +45,20 @@ public abstract class ThrownEnderpearlMixin extends ThrowableProjectileMixin { @ModifyArg(method = "onHit", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")) + target = "Lnet/minecraft/server/level/ServerPlayer;hurtServer(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;F)Z")) private float impl$onAttackEntityFromWithDamage(final float damage) { return (float) this.impl$damageAmount; } @Redirect(method = "onHit", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;changeDimension(Lnet/minecraft/world/level/portal/DimensionTransition;)Lnet/minecraft/world/entity/Entity;") + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;teleport(Lnet/minecraft/world/level/portal/TeleportTransition;)Lnet/minecraft/world/entity/Entity;") ) - private Entity impl$callMoveEntityEventForThrower(final Entity instance, final DimensionTransition $$0) { - final Entity entity = this.shadow$getOwner(); + private Entity impl$callMoveEntityEventForThrower(final Entity instance, final TeleportTransition $$0) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - frame.pushCause(entity); + frame.pushCause(instance); frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.ENDER_PEARL); - return instance.changeDimension($$0); + return instance.teleport($$0); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/VehicleEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/VehicleEntityMixin.java index 1fa0a765550..3ea934f0d1a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/VehicleEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/VehicleEntityMixin.java @@ -24,22 +24,24 @@ */ package org.spongepowered.common.mixin.core.world.entity.vehicle; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.vehicle.VehicleEntity; +import org.spongepowered.api.entity.Entity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; import org.spongepowered.common.mixin.core.world.entity.EntityMixin; -import org.spongepowered.common.util.DamageEventUtil; @Mixin(VehicleEntity.class) public abstract class VehicleEntityMixin extends EntityMixin { - @Inject(method = "hurt", cancellable = true, at = @At(value = "INVOKE", + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/vehicle/VehicleEntity;shouldSourceDestroy(Lnet/minecraft/world/damagesource/DamageSource;)Z")) - private void attackImpl$postOnAttackEntityFrom(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { - if (DamageEventUtil.callOtherAttackEvent((net.minecraft.world.entity.Entity) (Object) this, source, amount).isCancelled()) { + private void attack$onHurt(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { + if (SpongeDamageTracker.callDamageEvents((Entity) this, source, damage) == null) { cir.setReturnValue(true); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/BoatMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/boat/AbstractBoatMixin.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/BoatMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/boat/AbstractBoatMixin.java index a7403b7f54e..c5d72fe7604 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/BoatMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/boat/AbstractBoatMixin.java @@ -22,20 +22,20 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.vehicle; +package org.spongepowered.common.mixin.core.world.entity.vehicle.boat; -import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.entity.vehicle.boat.AbstractBoat; import org.spongepowered.api.data.Keys; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.Constant; import org.spongepowered.asm.mixin.injection.ModifyConstant; import org.spongepowered.common.bridge.data.SpongeDataHolderBridge; -import org.spongepowered.common.bridge.world.entity.vehicle.BoatBridge; +import org.spongepowered.common.bridge.world.entity.vehicle.AbstractBoatBridge; import org.spongepowered.common.mixin.core.world.entity.EntityMixin; import org.spongepowered.common.util.Constants; -@Mixin(Boat.class) -public abstract class BoatMixin extends EntityMixin implements BoatBridge { +@Mixin(AbstractBoat.class) +public abstract class AbstractBoatMixin extends EntityMixin implements AbstractBoatBridge { private float impl$maxSpeed = Constants.Entity.Boat.DEFAULT_MAX_SPEED; private boolean impl$moveOnLand = Constants.Entity.Boat.MOVE_ON_LAND; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/AbstractMinecartContainerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/AbstractMinecartContainerMixin.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/AbstractMinecartContainerMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/AbstractMinecartContainerMixin.java index 5cee1271dad..8d40d43a1f3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/AbstractMinecartContainerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/AbstractMinecartContainerMixin.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.vehicle; +package org.spongepowered.common.mixin.core.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.AbstractMinecartContainer; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecartContainer; import org.spongepowered.asm.mixin.Mixin; @Mixin(AbstractMinecartContainer.class) diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/AbstractMinecartMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/AbstractMinecartMixin.java similarity index 93% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/AbstractMinecartMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/AbstractMinecartMixin.java index deb37eba7df..a2be1bcb455 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/AbstractMinecartMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/AbstractMinecartMixin.java @@ -22,9 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.vehicle; +package org.spongepowered.common.mixin.core.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.AbstractMinecart; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecart; import net.minecraft.world.phys.Vec3; import org.spongepowered.api.data.Keys; import org.spongepowered.asm.mixin.Mixin; @@ -34,6 +35,7 @@ import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.common.bridge.data.SpongeDataHolderBridge; import org.spongepowered.common.bridge.world.entity.vehicle.AbstractMinecartBridge; +import org.spongepowered.common.mixin.core.world.entity.vehicle.VehicleEntityMixin; import org.spongepowered.common.util.Constants; import org.spongepowered.math.vector.Vector3d; @@ -54,7 +56,7 @@ public abstract class AbstractMinecartMixin extends VehicleEntityMixin implement * @reason Use our custom maximum speed for the Minecart. */ @Overwrite - protected double getMaxSpeed() { + protected double getMaxSpeed(ServerLevel level) { return this.impl$maxSpeed; } @@ -88,15 +90,6 @@ protected double getMaxSpeed() { return vec3d.multiply(this.impl$airborneMod.x(), this.impl$airborneMod.y(), this.impl$airborneMod.z()); } - @Redirect(method = "applyNaturalSlowdown", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/vehicle/AbstractMinecart;isVehicle()Z" - ) - ) - private boolean impl$applyDragIfEmpty(final AbstractMinecart self) { - return !this.impl$slowWhenEmpty || this.shadow$isVehicle(); - } - @Override public double bridge$getMaxSpeed() { return this.impl$maxSpeed; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartCommandBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartCommandBlockMixin.java similarity index 85% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartCommandBlockMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartCommandBlockMixin.java index 0a34add990e..5f4a6ab62d2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartCommandBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartCommandBlockMixin.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.vehicle; +package org.spongepowered.common.mixin.core.world.entity.vehicle.minecart; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.world.entity.vehicle.MinecartCommandBlock; +import net.minecraft.world.entity.vehicle.minecart.MinecartCommandBlock; import net.minecraft.world.level.BaseCommandBlock; import org.spongepowered.api.event.Cause; import org.spongepowered.api.service.permission.PermissionService; @@ -33,6 +33,7 @@ import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.commands.CommandSourceProviderBridge; import org.spongepowered.common.bridge.permissions.SubjectBridge; @@ -55,6 +56,9 @@ public abstract class MinecartCommandBlockMixin extends AbstractMinecartMixin im @Override public CommandSourceStack bridge$getCommandSource(final Cause cause) { - return this.commandBlock.createCommandSourceStack(); + if (this.level() instanceof net.minecraft.server.level.ServerLevel sl) { + return this.commandBlock.createCommandSourceStack(sl, SpongeCommon.server()); + } + return this.commandBlock.createCommandSourceStack(SpongeCommon.server().overworld(), SpongeCommon.server()); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartCommandBlock_MinecartCommandBaseMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartCommandBlock_MinecartCommandBaseMixin.java new file mode 100644 index 00000000000..25e5e41a1d4 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartCommandBlock_MinecartCommandBaseMixin.java @@ -0,0 +1,75 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity.vehicle.minecart; + +import net.kyori.adventure.audience.MessageType; +import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.text.Component; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.vehicle.minecart.MinecartCommandBlock; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.service.permission.Subject; +import org.spongepowered.api.service.permission.SubjectProxy; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.adventure.SpongeAdventure; +import org.spongepowered.common.mixin.core.world.BaseCommandBlockMixin; + +@Mixin(targets = "net.minecraft.world.entity.vehicle.minecart.MinecartCommandBlock$MinecartCommandBase") +public abstract class MinecartCommandBlock_MinecartCommandBaseMixin extends BaseCommandBlockMixin implements SubjectProxy { + + // @formatter:off + @Shadow @Final MinecartCommandBlock this$0; + // @formatter:on + + @Override + public Subject subject() { + return (Subject) this.this$0; + } + + @Override + public CommandSourceStack bridge$getCommandSource(final Cause cause) { + if (!(this.this$0.level() instanceof ServerLevel sl)) { + return null; + } + return this.createCommandSourceStack(sl, this.shadow$createSource(sl)); + } + + @Override + @SuppressWarnings({"deprecation", "UnstableApiUsage"}) + public void sendMessage(final @NonNull Identity identity, final @NonNull Component message, final @NonNull MessageType type) { + if (!(this.this$0.level() instanceof ServerLevel sl)) { + return; + } + final var source = this.shadow$createSource(sl); + if (source == null) { + return; + } + source.sendSystemMessage(SpongeAdventure.asVanilla(message)); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartFurnaceMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartFurnaceMixin.java similarity index 91% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartFurnaceMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartFurnaceMixin.java index a2c83ce6104..10f0b276bec 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartFurnaceMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartFurnaceMixin.java @@ -22,9 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.vehicle; +package org.spongepowered.common.mixin.core.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.MinecartFurnace; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.vehicle.minecart.MinecartFurnace; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.injection.At; @@ -47,9 +48,9 @@ private void onInit(CallbackInfo ci) { */ @Override @Overwrite - protected double getMaxSpeed() { + protected double getMaxSpeed(ServerLevel level) { // Return our custom value from EntityMinecart - return super.getMaxSpeed(); + return super.getMaxSpeed(level); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartTNTMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartTNTMixin.java similarity index 60% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartTNTMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartTNTMixin.java index e297f71806f..158f2490746 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/MinecartTNTMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/MinecartTNTMixin.java @@ -22,33 +22,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.vehicle; +package org.spongepowered.common.mixin.core.world.entity.vehicle.minecart; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.vehicle.MinecartTNT; -import net.minecraft.world.level.ExplosionDamageCalculator; -import net.minecraft.world.level.Level; +import net.minecraft.world.entity.vehicle.minecart.MinecartTNT; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; -import org.spongepowered.api.entity.vehicle.minecart.TNTMinecart; -import org.spongepowered.api.world.explosion.Explosion; -import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.bridge.explosives.ExplosiveBridge; import org.spongepowered.common.bridge.explosives.FusedExplosiveBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; -import org.spongepowered.common.event.SpongeCommonEventFactory; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTracker; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.util.Constants; -import org.spongepowered.common.util.DamageEventUtil; import java.util.Optional; @@ -58,21 +51,20 @@ public abstract class MinecartTNTMixin extends AbstractMinecartMixin implements // @formatter:off @Shadow private int fuse; @Shadow public abstract boolean shadow$isPrimed(); - @Shadow public abstract void shadow$primeFuse(); // @formatter:on - @Nullable private Integer impl$explosionRadius = null; + @Nullable private Float impl$explosionRadius = null; // override for fixed radius private int impl$fuseDuration = Constants.Entity.Minecart.DEFAULT_FUSE_DURATION; private boolean impl$detonationCancelled; @Nullable private Object impl$primeCause; @Override - public Optional bridge$getExplosionRadius() { + public Optional bridge$getExplosionRadius() { return Optional.ofNullable(this.impl$explosionRadius); } @Override - public void bridge$setExplosionRadius(final @Nullable Integer radius) { + public void bridge$setExplosionRadius(final @Nullable Float radius) { this.impl$explosionRadius = radius; } @@ -105,13 +97,14 @@ public abstract class MinecartTNTMixin extends AbstractMinecartMixin implements } - @Inject(method = "hurt", at = @At("HEAD")) - private void impl$onAttackSetPrimeCause(final DamageSource damageSource, final float amount, final CallbackInfoReturnable ci) { + @Inject(method = "hurtServer", at = @At("HEAD")) + private void impl$onAttackSetPrimeCause(final ServerLevel level, final DamageSource damageSource, + final float amount, final CallbackInfoReturnable ci) { this.impl$primeCause = damageSource; } - @Inject(method = "activateMinecart(IIIZ)V", at = @At("HEAD")) - private void impl$onActivateSetPrimeCauseNotifier(final int x, final int y, final int z, final boolean receivingPower, final CallbackInfo ci) { + @Inject(method = "activateMinecart(Lnet/minecraft/server/level/ServerLevel;IIIZ)V", at = @At("HEAD")) + private void impl$onActivateSetPrimeCauseNotifier(final ServerLevel level, final int x, final int y, final int z, final boolean receivingPower, final CallbackInfo ci) { if (((LevelBridge) this.shadow$level()).bridge$isFake()) { return; } @@ -140,28 +133,9 @@ public abstract class MinecartTNTMixin extends AbstractMinecartMixin implements } } - @Redirect( - method = "explode(Lnet/minecraft/world/damagesource/DamageSource;D)V", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;explode(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;Lnet/minecraft/world/level/ExplosionDamageCalculator;DDDFZLnet/minecraft/world/level/Level$ExplosionInteraction;)Lnet/minecraft/world/level/Explosion;" - ) - ) - private net.minecraft.world.level.@Nullable Explosion impl$useSpongeExplosion(final net.minecraft.world.level.Level world, final Entity entityIn, - final DamageSource damageSource, final ExplosionDamageCalculator calculator, - final double xIn, final double yIn, final double zIn, final float explosionRadius, final boolean fire, final Level.ExplosionInteraction modeIn) { - // TODO ExplosionDamageCalculator & fire - return SpongeCommonEventFactory.detonateExplosive(this, Explosion.builder() - .location(ServerLocation.of((ServerWorld) world, xIn, yIn, zIn)) - .sourceExplosive((TNTMinecart) this) - .radius(this.impl$explosionRadius != null ? this.impl$explosionRadius : explosionRadius) - .shouldPlaySmoke(modeIn.ordinal() > Level.ExplosionInteraction.NONE.ordinal()) - .shouldBreakBlocks(modeIn.ordinal() > Level.ExplosionInteraction.NONE.ordinal())) - .orElseGet(() -> { - this.impl$detonationCancelled = true; - return null; - } - ); + @Override + public void bridge$cancelExplosion() { + this.impl$detonationCancelled = true; } @Inject(method = "explode(Lnet/minecraft/world/damagesource/DamageSource;D)V", at = @At("RETURN")) @@ -172,10 +146,10 @@ public abstract class MinecartTNTMixin extends AbstractMinecartMixin implements } } - @Inject(method = "hurt", cancellable = true, at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/vehicle/MinecartTNT;explode(Lnet/minecraft/world/damagesource/DamageSource;D)V")) - private void attackImpl$postOnAttackEntityFrom(final DamageSource source, final float amount, final CallbackInfoReturnable cir) { - if (DamageEventUtil.callOtherAttackEvent((Entity) (Object) this, source, amount).isCancelled()) { + @Inject(method = "hurtServer", cancellable = true, at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/vehicle/minecart/MinecartTNT;explode(Lnet/minecraft/world/damagesource/DamageSource;D)V")) + private void attackImpl$postOnAttackEntityFrom(final ServerLevel level, final DamageSource source, final float damage, final CallbackInfoReturnable cir) { + if (SpongeDamageTracker.callDamageEvents((org.spongepowered.api.entity.Entity) this, source, damage) == null) { cir.setReturnValue(true); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/OldMinecartBehaviourMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/OldMinecartBehaviourMixin.java new file mode 100644 index 00000000000..88d6dd7e0cc --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/vehicle/minecart/OldMinecartBehaviourMixin.java @@ -0,0 +1,42 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity.vehicle.minecart; + +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecart; +import net.minecraft.world.entity.vehicle.minecart.OldMinecartBehavior; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.bridge.world.entity.vehicle.AbstractMinecartBridge; + +@Mixin(OldMinecartBehavior.class) +public abstract class OldMinecartBehaviourMixin { + + @Redirect(method = "getSlowdownFactor", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/vehicle/minecart/AbstractMinecart;isVehicle()Z")) + private boolean impl$applyDragIfEmpty(final AbstractMinecart minecart) { + return !((AbstractMinecartBridge) minecart).bridge$getSlowWhenEmpty() || minecart.isVehicle(); + } + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/food/FoodDataMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/food/FoodDataMixin.java index 952b97ffea6..2c0b454f21c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/food/FoodDataMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/food/FoodDataMixin.java @@ -91,18 +91,8 @@ public abstract class FoodDataMixin implements FoodDataBridge { this.saturationLevel = this.impl$fireEventAndGetValue(Keys.SATURATION, (double) this.saturationLevel, (double) value).floatValue(); } - @Redirect(method = "setExhaustion", at = @At(value = "FIELD", target = "Lnet/minecraft/world/food/FoodData;exhaustionLevel:F", opcode = Opcodes.PUTFIELD)) - private void impl$setExhaustion(final FoodData self, final float value) { - this.exhaustionLevel = this.impl$fireEventAndGetValue(Keys.EXHAUSTION, (double) this.exhaustionLevel, (double) value).floatValue(); - } - @Redirect(method = "addExhaustion", at = @At(value = "FIELD", target = "Lnet/minecraft/world/food/FoodData;exhaustionLevel:F", opcode = Opcodes.PUTFIELD)) - private void impl$addExhaustion(final FoodData self, final float value) { - this.exhaustionLevel = this.impl$fireEventAndGetValue(Keys.EXHAUSTION, (double) this.exhaustionLevel, (double) value).floatValue(); - } - - @Redirect(method = "tick", at = @At(value = "FIELD", target = "Lnet/minecraft/world/food/FoodData;exhaustionLevel:F", opcode = Opcodes.PUTFIELD)) - private void impl$tickDrainExhaustion(final FoodData self, final float value) { + private void impl$setExhaustion(final FoodData self, final float value) { this.exhaustionLevel = this.impl$fireEventAndGetValue(Keys.EXHAUSTION, (double) this.exhaustionLevel, (double) value).floatValue(); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/inventory/StonecutterMenuMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/inventory/StonecutterMenuMixin.java index be21fd71df1..6464a420aee 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/inventory/StonecutterMenuMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/inventory/StonecutterMenuMixin.java @@ -26,10 +26,10 @@ import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.ContainerListener; -import net.minecraft.world.inventory.DataSlot; import net.minecraft.world.inventory.Slot; import net.minecraft.world.inventory.StonecutterMenu; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.item.crafting.StonecutterRecipe; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -37,28 +37,33 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.common.accessor.world.inventory.AbstractContainerMenuAccessor; import org.spongepowered.common.item.recipe.stonecutting.SpongeStonecuttingRecipe; -import java.util.List; +import java.util.Optional; @Mixin(StonecutterMenu.class) public abstract class StonecutterMenuMixin { // @formatter:off @Shadow @Final Slot resultSlot; - @Shadow private List recipes; - @Shadow @Final private DataSlot selectedRecipeIndex; // @formatter:on - @Inject(method = "setupResultSlot", at = @At(value = "RETURN")) - private void impl$updateRecipeResultSlot(final CallbackInfo ci) { - if (!this.recipes.isEmpty() && this.recipes.get(this.selectedRecipeIndex.get()) instanceof SpongeStonecuttingRecipe) { - // For SpongeStonecuttingRecipe resend the output slot in case the actual crafting result differs from the exemplary result - final ItemStack stack = this.resultSlot.getItem(); - for (final ContainerListener listener : ((AbstractContainerMenuAccessor) this).accessor$containerListeners()) { - listener.slotChanged((AbstractContainerMenu) (Object) this, 1, stack); - } - } + @Inject(method = "setupResultSlot", + at = @At(value = "INVOKE", target = "Ljava/util/Optional;ifPresentOrElse(Ljava/util/function/Consumer;Ljava/lang/Runnable;)V", shift = At.Shift.AFTER), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void impl$updateRecipeResultSlot( + final int $$0, final CallbackInfo ci, + final Optional> recipe) { + recipe.filter(e -> e.value() instanceof SpongeStonecuttingRecipe) + .ifPresent(e -> { + // For SpongeStonecuttingRecipe resend the output slot in case the actual crafting result differs from the exemplary result + final ItemStack stack = this.resultSlot.getItem(); + for (final ContainerListener listener : ((AbstractContainerMenuAccessor) this).accessor$containerListeners()) { + listener.slotChanged((AbstractContainerMenu) (Object) this, 1, stack); + } + }); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ConsumableMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ConsumableMixin.java new file mode 100644 index 00000000000..6aa19ac7470 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ConsumableMixin.java @@ -0,0 +1,57 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.item; + +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.Consumable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.bridge.data.VanishableBridge; + +@Mixin(Consumable.class) +public abstract class ConsumableMixin { + + @Redirect(method = "emitParticlesAndSounds", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;spawnItemParticles(Lnet/minecraft/world/item/ItemStack;I)V")) + private void impl$createCauseFrameForTeleport(final LivingEntity instance, final ItemStack $$0, final int $$1) { + if (!(instance instanceof VanishableBridge bridge) || bridge.bridge$vanishState().createsParticles()) { + instance.spawnItemParticles($$0, $$1); + } + } + + + @Redirect(method = "emitParticlesAndSounds", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;playSound(Lnet/minecraft/sounds/SoundEvent;FF)V")) + private void impl$ignoreExperienceLevelSoundsWhileVanished(final LivingEntity instance, final SoundEvent soundEvent, + final float $$1, final float $$2) { + if (!(instance instanceof VanishableBridge bridge) || bridge.bridge$vanishState().createsSounds()) { + instance.playSound(soundEvent, $$1, $$2); + } + } + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/EnderEyeItemMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/EnderEyeItemMixin.java index a18bc337af1..6d25af9dfb0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/EnderEyeItemMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/EnderEyeItemMixin.java @@ -28,7 +28,6 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; -import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.EnderEyeItem; @@ -90,14 +89,14 @@ public abstract class EnderEyeItemMixin { cancellable = true ) private void impl$ThrowForPreEvent(final Level worldIn, final Player playerIn, final InteractionHand handIn, - final CallbackInfoReturnable> cir, final ItemStack used, final BlockHitResult rayTraceResult, final ServerLevel world, final BlockPos targetPos) { + final CallbackInfoReturnable cir, final ItemStack used, final BlockHitResult rayTraceResult, final ServerLevel world, final BlockPos targetPos) { if (targetPos != null && !((LevelBridge) worldIn).bridge$isFake() && ShouldFire.CONSTRUCT_ENTITY_EVENT_PRE) { final ConstructEntityEvent.Pre event = SpongeEventFactory.createConstructEntityEventPre(PhaseTracker.getInstance().currentCause(), ServerLocation.of((ServerWorld) worldIn, playerIn.getX(), playerIn.getY() + (double) (playerIn.getDimensions(playerIn .getPose()).height() / 2.0F), playerIn.getZ()), new Vector3d(0, 0, 0), EntityTypes.EYE_OF_ENDER.get()); if (SpongeCommon.post(event)) { - cir.setReturnValue(new InteractionResultHolder<>(InteractionResult.SUCCESS, used)); + cir.setReturnValue(InteractionResult.SUCCESS_SERVER); } } } @@ -108,14 +107,14 @@ public abstract class EnderEyeItemMixin { @SuppressWarnings("Duplicates") @Surrogate private void impl$ThrowForPreEvent(final Level worldIn, final Player playerIn, final InteractionHand handIn, - final CallbackInfoReturnable> cir, final ItemStack used, @Nullable final BlockPos targetPos) { + final CallbackInfoReturnable cir, final ItemStack used, @Nullable final BlockPos targetPos) { if (targetPos != null && !((LevelBridge) worldIn).bridge$isFake() && ShouldFire.CONSTRUCT_ENTITY_EVENT_PRE) { final ConstructEntityEvent.Pre event = SpongeEventFactory.createConstructEntityEventPre(PhaseTracker.getInstance().currentCause(), ServerLocation.of((ServerWorld) worldIn, playerIn.getX(), playerIn.getY() + (double) (playerIn.getDimensions(playerIn .getPose()).height() / 2.0F), playerIn.getZ()), new Vector3d(0, 0, 0), EntityTypes.EYE_OF_ENDER.get()); if (SpongeCommon.post(event)) { - cir.setReturnValue(new InteractionResultHolder<>(InteractionResult.SUCCESS, used)); + cir.setReturnValue(InteractionResult.SUCCESS_SERVER); } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/FireworkRocketItemMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/FireworkRocketItemMixin.java index 60c2282d36e..4556dd78cb2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/FireworkRocketItemMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/FireworkRocketItemMixin.java @@ -24,12 +24,11 @@ */ package org.spongepowered.common.mixin.core.world.item; -import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; -import net.minecraft.world.InteractionResultHolder; -import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.FireworkRocketEntity; +import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.item.FireworkRocketItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; @@ -87,9 +86,9 @@ public abstract class FireworkRocketItemMixin { cancellable = true ) private void impl$throwPreBeforeSpawning(final Level worldIn, final net.minecraft.world.entity.player.Player playerIn, final InteractionHand handIn, - final CallbackInfoReturnable> cir, final ItemStack stack) { + final CallbackInfoReturnable cir, final ItemStack stack) { if (this.impl$throwConstructPreEvent(worldIn, playerIn, stack)) { - cir.setReturnValue(new InteractionResultHolder<>(InteractionResult.SUCCESS, stack)); + cir.setReturnValue(InteractionResult.SUCCESS); } } @@ -117,8 +116,8 @@ public abstract class FireworkRocketItemMixin { /** * Private method for bridging the duplicate between - * {@link #spongeImpl$ThrowPreBeforeSpawning(World, PlayerEntity, Hand, CallbackInfoReturnable, ItemStack)} and - * {@link #spongeImpl$ThrowPrimeEventsIfCancelled(ItemUseContext, CallbackInfoReturnable)} + * {@link #impl$throwPreBeforeSpawning} and + * {@link #impl$throwPrimeEventsIfCancelled} * since both follow the same logic, but differ in how they are called. * * @param world The world @@ -141,41 +140,53 @@ public abstract class FireworkRocketItemMixin { return false; } + private boolean impl$useOnCancelled = false; + + @Redirect(method = "useOn", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/Projectile;spawnProjectile(Lnet/minecraft/world/entity/projectile/Projectile;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/projectile/Projectile;") + ) + private T impl$onSpawnProjectileForUseOn(final T rocket, final ServerLevel $$1, final ItemStack usedItem, final UseOnContext context) { + this.impl$useOnCancelled = this.impl$throwPrimeEventAndGetCancel(context.getLevel(), context.getPlayer(), (FireworkRocketEntity) rocket, usedItem); + if (!this.impl$useOnCancelled) { + return Projectile.spawnProjectile(rocket, $$1, usedItem); + } + return rocket; + } + @Inject(method = "useOn", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z"), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/Projectile;spawnProjectile(Lnet/minecraft/world/entity/projectile/Projectile;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/projectile/Projectile;", + shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true ) - private void impl$injectPrimeEventAndCancel(final UseOnContext context, final CallbackInfoReturnable cir, final Level world, - final ItemStack usedItem, final net.minecraft.world.phys.Vec3 vec3d, final Direction direction, final FireworkRocketEntity rocket) { - if (this.impl$throwPrimeEventAndGetCancel(context.getLevel(), context.getPlayer(), rocket, usedItem)) { + private void impl$afterSpawnProjectileForUseOn(final UseOnContext context, final CallbackInfoReturnable cir) { + if (this.impl$useOnCancelled) { cir.setReturnValue(InteractionResult.SUCCESS); } } - private FireworkRocketEntity impl$capturedRocket; + private boolean impl$useCancelled = false; - @Redirect(method = "use", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z")) - private boolean impl$captureFireworkRocket(Level world, Entity p_217376_1_) { - this.impl$capturedRocket = (FireworkRocketEntity) p_217376_1_; - return true; + @Redirect(method = "use", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/Projectile;spawnProjectile(Lnet/minecraft/world/entity/projectile/Projectile;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/projectile/Projectile;")) + private T impl$onSpawnProjectileForUse(final T rocket, final ServerLevel serverLevel, final ItemStack usedItem, + final Level $$0, final net.minecraft.world.entity.player.Player player, InteractionHand $$2) { + this.impl$useCancelled = this.impl$throwPrimeEventAndGetCancel(serverLevel, player, (FireworkRocketEntity) rocket, usedItem); + if (!this.impl$useCancelled) { + return Projectile.spawnProjectile(rocket, serverLevel, usedItem); + } + return rocket; } - @Inject(method = "use(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResultHolder;", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z", shift = At.Shift.AFTER), - locals = LocalCapture.CAPTURE_FAILHARD, + @Inject(method = "use", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/Projectile;spawnProjectile(Lnet/minecraft/world/entity/projectile/Projectile;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/projectile/Projectile;", + shift = At.Shift.AFTER), cancellable = true ) - private void impl$injectPrimeEventAndCancel(final Level worldIn, final net.minecraft.world.entity.player.Player player, final InteractionHand handIn, - final CallbackInfoReturnable> cir, final ItemStack usedItem) { - if (this.impl$throwPrimeEventAndGetCancel(worldIn, player, this.impl$capturedRocket, usedItem)) { - this.impl$capturedRocket = null; - // We have to still return success because the server/client can get out of sync otherwise. - cir.setReturnValue(new InteractionResultHolder<>(InteractionResult.SUCCESS, usedItem)); + private void impl$afterSpawnProjectileForUse(final Level $$0, final net.minecraft.world.entity.player.Player $$1, + final InteractionHand $$2, final CallbackInfoReturnable cir) { + if (this.impl$useCancelled) { + cir.setReturnValue(InteractionResult.SUCCESS); } - - worldIn.addFreshEntity(this.impl$capturedRocket); - this.impl$capturedRocket = null; } /** @@ -183,8 +194,8 @@ public abstract class FireworkRocketItemMixin { * the prime is cancelled, then the firework will not be spawned. * This is to bridge the same logic between * - * {@link #impl$injectPrimeEventAndCancel(UseOnContext, CallbackInfoReturnable, Level, ItemStack, net.minecraft.world.phys.Vec3, Direction, FireworkRocketEntity)} - * {@link #impl$injectPrimeEventAndCancel(Level, net.minecraft.world.entity.player.Player, InteractionHand, CallbackInfoReturnable, ItemStack)} + * {@link #impl$onSpawnProjectileForUseOn} + * {@link #impl$onSpawnProjectileForUse} * * @param world The world * @param player The player using the item diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/FishingRodItemMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/FishingRodItemMixin.java index eb0f7571066..27004da8cd2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/FishingRodItemMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/FishingRodItemMixin.java @@ -29,7 +29,6 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; -import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.FishingHook; @@ -44,6 +43,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.event.tracking.PhaseTracker; @@ -53,26 +53,32 @@ public abstract class FishingRodItemMixin { @Inject(method = "use", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/entity/projectile/FishingHook;retrieve(Lnet/minecraft/world/item/ItemStack;)I"), cancellable = true) - private void impl$cancelHookRetraction(Level world, Player player, InteractionHand hand, CallbackInfoReturnable> cir) { + private void impl$cancelHookRetraction(Level world, Player player, InteractionHand hand, CallbackInfoReturnable cir) { if (player.fishing != null) { - // Event was cancelled - cir.setReturnValue(new InteractionResultHolder<>(InteractionResult.SUCCESS, player.getItemInHand(hand))); + cir.setReturnValue(InteractionResult.SUCCESS); // Event was cancelled } } - @Inject(method = "use", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V", ordinal = 1), - cancellable = true) - private void impl$onThrowEvent(Level level, Player player, InteractionHand hand, CallbackInfoReturnable> cir, + @Inject(method = "use", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/Entity;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V" + ), + slice = @Slice( + from = @At(value = "FIELD", target = "Lnet/minecraft/world/level/gameevent/GameEvent;ITEM_INTERACT_FINISH:Lnet/minecraft/core/Holder$Reference;"), + to = @At(value = "CONSTANT", args = "classValue=net/minecraft/server/level/ServerLevel") + ), + cancellable = true) + private void impl$onThrowEvent(Level level, Player player, InteractionHand hand, CallbackInfoReturnable cir, @Share("entity") LocalRef entityRef) { - if (level.isClientSide) { + if (level.isClientSide()) { // Only fire event on server-side to avoid crash on client return; } ItemStack itemstack = player.getItemInHand(hand); if (level instanceof ServerLevel serverLevel) { - int $$6 = (int)(EnchantmentHelper.getFishingTimeReduction(serverLevel, itemstack, player) * 20.0F); + int $$6 = (int) (EnchantmentHelper.getFishingTimeReduction(serverLevel, itemstack, player) * 20.0F); int $$7 = EnchantmentHelper.getFishingLuckBonus(serverLevel, itemstack, player); FishingHook fishHook = new FishingHook(player, level, $$7, $$6); @@ -80,7 +86,7 @@ public abstract class FishingRodItemMixin { frame.pushCause(player); if (SpongeCommon.post(SpongeEventFactory.createFishingEventStart(PhaseTracker.getInstance().currentCause(), (FishingBobber) fishHook))) { fishHook.remove(Entity.RemovalReason.DISCARDED); // Bye - cir.setReturnValue(new InteractionResultHolder<>(InteractionResult.SUCCESS, player.getItemInHand(hand))); + cir.setReturnValue(InteractionResult.SUCCESS); } else { entityRef.set(fishHook); } @@ -89,7 +95,7 @@ public abstract class FishingRodItemMixin { } @Redirect(method = "use", at = @At(value = "NEW", target = "net/minecraft/world/entity/projectile/FishingHook")) - private FishingHook impl$onNewEntityFishHook(Player p_i50220_1_, Level p_i50220_2_, int p_i50220_3_, int p_i50220_4_, + private FishingHook impl$onNewEntityFishHook(final Player $$0, final Level $$1, final int $$2, final int $$3, @Share("entity") LocalRef entityRef) { // Use the fish hook we created for the event return entityRef.get(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemCooldownsMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemCooldownsMixin.java index a7d72253a0e..ca570f63e46 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemCooldownsMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemCooldownsMixin.java @@ -24,9 +24,10 @@ */ package org.spongepowered.common.mixin.core.world.item; +import net.minecraft.resources.Identifier; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemCooldowns; -import org.spongepowered.api.item.ItemType; +import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -49,12 +50,15 @@ public abstract class ItemCooldownsMixin implements ItemCooldownsBridge { // @formatter:off @Shadow @Final private Map cooldowns; + @Shadow public abstract Identifier shadow$getCooldownGroup(final ItemStack $$0); + // @formatter:on + private int impl$lastSetCooldownResult; @Inject( - method = "addCooldown", + method = "addCooldown(Lnet/minecraft/resources/Identifier;I)V", at = @At( value = "HEAD", remap = false @@ -62,15 +66,15 @@ public abstract class ItemCooldownsMixin implements ItemCooldownsBridge { locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true ) - private void impl$throwEventOnSetAndTrackResult(final Item item, final int ticks, final CallbackInfo ci) { - this.impl$lastSetCooldownResult = this.impl$throwSetCooldownEvent((ItemType) item, ticks); + private void impl$throwEventOnSetAndTrackResult(final Identifier group, final int ticks, final CallbackInfo ci) { + this.impl$lastSetCooldownResult = this.impl$throwSetCooldownEvent(group, ticks); if (this.impl$lastSetCooldownResult == Constants.Sponge.Entity.Player.ITEM_COOLDOWN_CANCELLED) { ci.cancel(); } } @ModifyVariable( - method = "addCooldown", + method = "addCooldown(Lnet/minecraft/resources/Identifier;I)V", at = @At( value = "HEAD", remap = false @@ -82,16 +86,16 @@ public abstract class ItemCooldownsMixin implements ItemCooldownsBridge { } @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Ljava/util/Map$Entry;getKey()Ljava/lang/Object;", remap = false)) - private Object onTick(final Map.Entry entry) { - this.impl$throwEndCooldownEvent((ItemType) entry.getKey()); + private Object onTick(final Map.Entry entry) { + this.impl$throwEndCooldownEvent(entry.getKey()); return entry.getKey(); } - protected int impl$throwSetCooldownEvent(final ItemType type, final int ticks) { + protected int impl$throwSetCooldownEvent(final Identifier group, final int ticks) { return 0; } - protected void impl$throwEndCooldownEvent(final ItemType type) { + protected void impl$throwEndCooldownEvent(final Identifier group) { } @@ -101,7 +105,8 @@ private Object onTick(final Map.Entry entry) { } @Inject(method = "getCooldownPercent", at = @At("HEAD"), cancellable = true) - private void impl$getCooldownPercentInfiniteCooldown(final Item $$0, final float $$1, final CallbackInfoReturnable cir) { + private void impl$getCooldownPercentInfiniteCooldown(final ItemStack $$0, final float $$1, final CallbackInfoReturnable cir) { + Identifier $$2 = this.shadow$getCooldownGroup($$0); final ItemCooldowns_CooldownInstanceAccessor cooldown = (ItemCooldowns_CooldownInstanceAccessor) this.cooldowns.get($$0); if (cooldown != null && cooldown.accessor$endTime() == cooldown.accessor$startTime() - 1) { cir.setReturnValue(1.0F); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemStackMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemStackMixin.java index 6e5e8a83911..edb7e765228 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemStackMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemStackMixin.java @@ -57,12 +57,11 @@ public abstract class ItemStackMixin implements SpongeDataHolderBridge, DataComp // @formatter:off @Shadow public abstract boolean shadow$isEmpty(); - @Shadow @Final private PatchedDataComponentMap components; + @Shadow @Final PatchedDataComponentMap components; // @formatter:on - @Override public Optional bridge$get(final Key<@NonNull ? extends Value> key) { if (this.shadow$isEmpty()) { @@ -91,7 +90,7 @@ public abstract class ItemStackMixin implements SpongeDataHolderBridge, DataComp public CompoundTag data$getCompound() { final @Nullable CustomData customData = this.components.get(DataComponents.CUSTOM_DATA); if (customData != null && customData != CustomData.EMPTY) { - return customData.getUnsafe(); + return ((CustomDataAccessor) (Object) customData).accessor$tag(); } return null; } @@ -115,7 +114,7 @@ public abstract class ItemStackMixin implements SpongeDataHolderBridge, DataComp } // Read custom data from nbt - @Inject(method = "(Lnet/minecraft/world/level/ItemLike;ILnet/minecraft/core/component/PatchedDataComponentMap;)V", at = @At("RETURN")) + @Inject(method = "(Lnet/minecraft/core/Holder;ILnet/minecraft/core/component/PatchedDataComponentMap;)V", at = @At("RETURN")) private void impl$onRead(final CallbackInfo ci) { if (!this.shadow$isEmpty()) { DataUtil.syncTagToData(this); // Deserialize @@ -123,7 +122,7 @@ public abstract class ItemStackMixin implements SpongeDataHolderBridge, DataComp } } - @Inject(method = "set", at = @At("RETURN")) + @Inject(method = "set(Lnet/minecraft/core/component/DataComponentType;Ljava/lang/Object;)Ljava/lang/Object;", at = @At("RETURN")) private void impl$onSetCustomData(final DataComponentType type, final T value, final CallbackInfoReturnable cir) { if (type.equals(DataComponents.CUSTOM_DATA)) { this.bridge$clear(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/MapItemMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/MapItemMixin.java index 0563739bcbc..e1bc6f99b6c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/MapItemMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/MapItemMixin.java @@ -24,7 +24,9 @@ */ package org.spongepowered.common.mixin.core.world.item; +import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.MapItem; import net.minecraft.world.level.Level; import net.minecraft.world.level.saveddata.maps.MapId; @@ -33,17 +35,19 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.common.bridge.world.storage.MapItemSavedDataBridge; @Mixin(MapItem.class) public class MapItemMixin { - @Inject(method = "createNewSavedData", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/Level;setMapData(Lnet/minecraft/world/level/saveddata/maps/MapId;Lnet/minecraft/world/level/saveddata/maps/MapItemSavedData;)V"), - locals = LocalCapture.CAPTURE_FAILHARD) + @Inject(method = "createNewSavedData", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/level/ServerLevel;setMapData(Lnet/minecraft/world/level/saveddata/maps/MapId;Lnet/minecraft/world/level/saveddata/maps/MapItemSavedData;)V" + ) + ) private static void impl$storeMapId( - final Level level, + final ServerLevel level, final int x, final int y, final int scale, @@ -51,8 +55,8 @@ public class MapItemMixin { final boolean unlimitedTracking, final ResourceKey dimension, final CallbackInfoReturnable cir, - final MapItemSavedData data, - final MapId id + final @Local MapItemSavedData data, + final @Local MapId id ) { ((MapItemSavedDataBridge) data).bridge$initMapId(id.id()); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ServerItemCooldownsMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ServerItemCooldownsMixin.java index 182d83ab56d..3799930c8be 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ServerItemCooldownsMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ServerItemCooldownsMixin.java @@ -24,14 +24,14 @@ */ package org.spongepowered.common.mixin.core.world.item; -import net.minecraft.world.item.Item; +import net.minecraft.resources.Identifier; import net.minecraft.world.item.ServerItemCooldowns; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; import org.spongepowered.api.entity.living.player.CooldownTracker; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.entity.living.player.CooldownEvent; -import org.spongepowered.api.item.ItemType; import org.spongepowered.api.util.Ticks; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -50,20 +50,20 @@ public abstract class ServerItemCooldownsMixin extends ItemCooldownsMixin { // @formmater:off @Shadow @Final private net.minecraft.server.level.ServerPlayer player; - @Shadow protected abstract void shadow$onCooldownStarted(Item itemIn, int ticksIn); + @Shadow protected abstract void shadow$onCooldownStarted(Identifier group, int ticksIn); // @formmater:on @Override - protected int impl$throwSetCooldownEvent(final ItemType type, final int ticks) { + protected int impl$throwSetCooldownEvent(final Identifier group, final int ticks) { if (ticks == 0) { return 0; } - final Optional beforeCooldown = ((CooldownTracker) this).cooldown(type); + final Optional beforeCooldown = ((CooldownTracker) this).cooldown((ResourceKey) (Object)group); final CooldownEvent.Set event = SpongeEventFactory.createCooldownEventSet(PhaseTracker.getInstance().currentCause(), - SpongeTicks.ticksOrInfinite(ticks), SpongeTicks.ticksOrInfinite(ticks), type, (ServerPlayer) this.player, beforeCooldown); + SpongeTicks.ticksOrInfinite(ticks), SpongeTicks.ticksOrInfinite(ticks), (ResourceKey) (Object) group, (ServerPlayer) this.player, beforeCooldown); if (Sponge.eventManager().post(event)) { - this.shadow$onCooldownStarted((Item) type, beforeCooldown.map(x -> (int) x.ticks()).orElse(0)); + this.shadow$onCooldownStarted(group, beforeCooldown.map(x -> (int) x.ticks()).orElse(0)); return Constants.Sponge.Entity.Player.ITEM_COOLDOWN_CANCELLED; } else { return SpongeTicks.toSaturatedIntOrInfinite(event.newCooldown()); @@ -71,9 +71,9 @@ public abstract class ServerItemCooldownsMixin extends ItemCooldownsMixin { } @Override - protected void impl$throwEndCooldownEvent(final ItemType type) { + protected void impl$throwEndCooldownEvent(final Identifier group) { final CooldownEvent.End event = SpongeEventFactory.createCooldownEventEnd(PhaseTracker.getInstance().currentCause(), - type, (ServerPlayer) this.player); + (ResourceKey) (Object) group, (ServerPlayer) this.player); Sponge.eventManager().post(event); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/AbstractCookingRecipeMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/AbstractCookingRecipeMixin.java index f2a11346edf..b0bf7289762 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/AbstractCookingRecipeMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/AbstractCookingRecipeMixin.java @@ -24,25 +24,16 @@ */ package org.spongepowered.common.mixin.core.world.item.crafting; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.AbstractCookingRecipe; -import org.spongepowered.asm.mixin.Final; +import net.minecraft.world.item.crafting.Ingredient; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.bridge.world.item.crafting.RecipeResultBridge; +import org.spongepowered.common.bridge.world.item.crafting.CookingRecipeBridge; @Mixin(AbstractCookingRecipe.class) -public abstract class AbstractCookingRecipeMixin implements RecipeResultBridge { - - // @formatter=off - @Shadow @Final protected ItemStack result; - - // @formatter=on +public abstract class AbstractCookingRecipeMixin extends SingleItemRecipeMixin implements CookingRecipeBridge { @Override - public ItemStack bridge$result() { - return this.result; + public Ingredient bridge$ingredient() { + return this.shadow$input(); } - - } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/IngredientMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/IngredientMixin.java index ade3ce146d4..07dda2bd02b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/IngredientMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/IngredientMixin.java @@ -27,26 +27,33 @@ import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import net.minecraft.world.item.crafting.Ingredient; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.item.recipe.ingredient.SpongeIngredient; @Mixin(Ingredient.class) public abstract class IngredientMixin { - @Inject(method = "codec", at = @At(value = "RETURN"), cancellable = true) - private static void impl$modifyCodec(final boolean $$0, final CallbackInfoReturnable> cir) { - final Codec original = cir.getReturnValue(); - var combinedCodec = Codec.xor(SpongeIngredient.CODEC, original).xmap(to -> to.map(si -> si, i -> i), - fr -> { - if (fr instanceof SpongeIngredient si) { - return Either.left(si); - } else { - return Either.right(fr); - } - }); - cir.setReturnValue(combinedCodec); + //@formatter:off + @Shadow @Final @Mutable public static Codec CODEC; + //@formatter:on + + @Redirect( + method = "", + at = @At(value = "FIELD", target = "Lnet/minecraft/world/item/crafting/Ingredient;CODEC:Lcom/mojang/serialization/Codec;") + ) + private static void impl$modifyCodec(Codec value) { + CODEC = Codec.xor(SpongeIngredient.CODEC, value).xmap(to -> to.map(si -> si, i -> i), + fr -> { + if (fr instanceof SpongeIngredient si) { + return Either.left(si); + } else { + return Either.right(fr); + } + }); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/RecipeManagerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/RecipeManagerMixin.java index 15ca24ce109..6a88a92d0e8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/RecipeManagerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/RecipeManagerMixin.java @@ -24,10 +24,12 @@ */ package org.spongepowered.common.mixin.core.world.item.crafting; +import com.google.gson.JsonObject; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.core.HolderLookup; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeManager; import org.spongepowered.asm.mixin.Mixin; @@ -37,20 +39,25 @@ @Mixin(RecipeManager.class) public abstract class RecipeManagerMixin { -@Redirect(method = "fromJson", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/Codec;parse(Lcom/mojang/serialization/DynamicOps;Ljava/lang/Object;)Lcom/mojang/serialization/DataResult;")) - private static DataResult> impl$onParseRecipe(final Codec> instance, final DynamicOps dynamicOps, final T element, final ResourceLocation $$0) -{ - final DataResult> parsed; - try { - parsed = instance.parse(dynamicOps, element); - } catch (Exception e) { - SpongeCommon.logger().error("Could not parse recipe {}", $$0, e); - throw new RuntimeException(e); + @Redirect(method = "fromJson", at = @At( + value = "INVOKE", + target = "Lcom/mojang/serialization/Codec;parse(Lcom/mojang/serialization/DynamicOps;Ljava/lang/Object;)Lcom/mojang/serialization/DataResult;" + )) + private static DataResult> impl$onParseRecipe( + final Codec> instance, final DynamicOps dynamicOps, final T element, + ResourceKey> $$0, JsonObject $$1, HolderLookup.Provider $$2 + ) { + final DataResult> parsed; + try { + parsed = instance.parse(dynamicOps, element); + } catch (Exception e) { + SpongeCommon.logger().error("Could not parse recipe {}", $$0, e); + throw new RuntimeException(e); + } + if (parsed.error().isPresent()) { + SpongeCommon.logger().error("Could not parse recipe {} {}", $$0, parsed.error().get().message()); + } + return parsed; } - if (parsed.error().isPresent()) { - SpongeCommon.logger().error("Could not parse recipe {} {}", $$0, parsed.error().get().message()); - } - return parsed; -} } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/RecipeManagerMixin_Shared.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/RecipeManagerMixin_Shared.java new file mode 100644 index 00000000000..2601d91e40c --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/RecipeManagerMixin_Shared.java @@ -0,0 +1,67 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.item.crafting; + +import com.google.gson.JsonElement; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DynamicOps; +import net.minecraft.resources.FileToIdConverter; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeManager; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.registry.RegistryHolderLogic; +import org.spongepowered.common.registry.SpongeRegistryHolder; + +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.stream.Stream; + +@Mixin(RecipeManager.class) +public abstract class RecipeManagerMixin_Shared { + + @SuppressWarnings("unchecked") + @WrapOperation(method = "prepare(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)Lnet/minecraft/world/item/crafting/RecipeMap;", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/SimpleJsonResourceReloadListener;scanDirectory(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/resources/FileToIdConverter;Lcom/mojang/serialization/DynamicOps;Lcom/mojang/serialization/Codec;Ljava/util/Map;)V")) + private void impl$onPrepare(final ResourceManager $$0, final FileToIdConverter $$1, final DynamicOps $$2, final Codec> $$3, + final Map> $$4, final Operation original) { + SortedMap> result = new TreeMap<>(); + original.call($$0, $$1, $$2, $$3, result); + final RegistryHolderLogic registryHolder = ((SpongeRegistryHolder) $$0).registryHolder(); + final Registry> registry = (Registry>) (Object) registryHolder.registry(RegistryTypes.RECIPE); + result.forEach((k, v) -> registry.register((org.spongepowered.api.ResourceKey) (Object) k, v)); + Launch.instance().lifecycle().processServerRegistries((RegistryHolder) $$0, Stream.of(registry)); + registry.streamEntries().forEach(e -> $$4.put((Identifier) (Object) e.key(), e.value())); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapedRecipeMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapedRecipeMixin.java index dc3d3956890..ed0e11f615b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapedRecipeMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapedRecipeMixin.java @@ -24,7 +24,9 @@ */ package org.spongepowered.common.mixin.core.world.item.crafting; +import net.minecraft.core.component.DataComponentPatch; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.ShapedRecipe; import net.minecraft.world.item.crafting.ShapedRecipePattern; import org.spongepowered.asm.mixin.Final; @@ -36,8 +38,8 @@ public abstract class ShapedRecipeMixin implements ShapedRecipeBridge { // @formatter=off - @Shadow @Final ItemStack result; - @Shadow @Final ShapedRecipePattern pattern; + @Shadow @Final private ItemStackTemplate result; + @Shadow @Final private ShapedRecipePattern pattern; // @formatter=on @@ -49,7 +51,7 @@ public abstract class ShapedRecipeMixin implements ShapedRecipeBridge { @Override public ItemStack bridge$result() { - return this.result; + return this.result.apply(DataComponentPatch.EMPTY); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapedRecipe_SerializerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapedRecipe_SerializerMixin.java deleted file mode 100644 index 10af39bc8f3..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapedRecipe_SerializerMixin.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.item.crafting; - -import com.mojang.datafixers.kinds.App; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.world.item.crafting.ShapedRecipe; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.item.recipe.crafting.shaped.SpongeShapedRecipe; - -import java.util.function.Function; - -@Mixin(ShapedRecipe.Serializer.class) -public abstract class ShapedRecipe_SerializerMixin { - - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/codecs/RecordCodecBuilder;mapCodec(Ljava/util/function/Function;)Lcom/mojang/serialization/MapCodec;")) - private static MapCodec impl$onCreateCodec(final Function, ? extends App, ShapedRecipe>> builder) { - final var mcMapCodec = RecordCodecBuilder.mapCodec(builder); - return Codec.mapEither(SpongeShapedRecipe.SPONGE_CODEC, mcMapCodec).xmap(to -> to.map(si -> si, i -> i), - fr -> { - if (fr instanceof SpongeShapedRecipe si) { - return Either.left(si); - } - return Either.right(fr); - }); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapelessRecipeMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapelessRecipeMixin.java index 25d9dd221c1..3f89c899b33 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapelessRecipeMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapelessRecipeMixin.java @@ -24,7 +24,9 @@ */ package org.spongepowered.common.mixin.core.world.item.crafting; +import net.minecraft.core.component.DataComponentPatch; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.ShapelessRecipe; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -35,13 +37,13 @@ public abstract class ShapelessRecipeMixin implements RecipeResultBridge { // @formatter=off - @Shadow @Final ItemStack result; + @Shadow @Final private ItemStackTemplate result; // @formatter=on @Override public ItemStack bridge$result() { - return this.result; + return this.result.apply(DataComponentPatch.EMPTY); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapelessRecipe_SerializerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapelessRecipe_SerializerMixin.java deleted file mode 100644 index a8bf6cacf16..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/ShapelessRecipe_SerializerMixin.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.item.crafting; - -import com.mojang.datafixers.kinds.App; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.world.item.crafting.ShapelessRecipe; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.item.recipe.crafting.shapeless.SpongeShapelessRecipe; - -import java.util.function.Function; - -@Mixin(ShapelessRecipe.Serializer.class) -public abstract class ShapelessRecipe_SerializerMixin { - - - @Redirect(method = "", at = @At(value = "INVOKE", - target = "Lcom/mojang/serialization/codecs/RecordCodecBuilder;mapCodec(Ljava/util/function/Function;)Lcom/mojang/serialization/MapCodec;")) - private static MapCodec impl$onCreateCodec( - final Function, ? extends App, ShapelessRecipe>> builder) { - final var mcMapCodec = RecordCodecBuilder.mapCodec(builder); - return Codec.mapEither(SpongeShapelessRecipe.SPONGE_CODEC, mcMapCodec).xmap(to -> to.map(si -> si, i -> i), - fr -> { - if (fr instanceof SpongeShapelessRecipe si) { - return Either.left(si); - } - return Either.right(fr); - }); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SimpleCookingSerializerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SimpleCookingSerializerMixin.java deleted file mode 100644 index 1ff1a6a7ddb..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SimpleCookingSerializerMixin.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.item.crafting; - -import static org.spongepowered.common.util.Constants.Recipe.SPONGE_TYPE; - -import com.mojang.datafixers.kinds.App; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.AbstractCookingRecipe; -import net.minecraft.world.item.crafting.BlastingRecipe; -import net.minecraft.world.item.crafting.CampfireCookingRecipe; -import net.minecraft.world.item.crafting.CookingBookCategory; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.SimpleCookingSerializer; -import net.minecraft.world.item.crafting.SmeltingRecipe; -import net.minecraft.world.item.crafting.SmokingRecipe; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.bridge.world.item.crafting.RecipeResultBridge; -import org.spongepowered.common.item.recipe.ResultFunctionRecipe; -import org.spongepowered.common.item.recipe.cooking.CookingRecipeFactory; -import org.spongepowered.common.item.recipe.cooking.SpongeBlastingRecipe; -import org.spongepowered.common.item.recipe.cooking.SpongeCampfireCookingRecipe; -import org.spongepowered.common.item.recipe.cooking.SpongeSmeltingRecipe; -import org.spongepowered.common.item.recipe.cooking.SpongeSmokingRecipe; -import org.spongepowered.common.util.Constants; - -import java.util.function.Function; - -@Mixin(SimpleCookingSerializer.class) -public abstract class SimpleCookingSerializerMixin { - - @Redirect(method = "", at = @At(value = "INVOKE", - target = "Lcom/mojang/serialization/codecs/RecordCodecBuilder;mapCodec(Ljava/util/function/Function;)Lcom/mojang/serialization/MapCodec;")) - private MapCodec impl$onCreateCodec( - final Function, ? extends App, T>> builder, - final AbstractCookingRecipe.Factory $$0, - final int defaultCookingTime) { - final var mcMapCodec = RecordCodecBuilder.mapCodec(builder); - - final T constructed = $$0.create(null, null, null, null, 0, 0); - if (constructed instanceof BlastingRecipe) { - var spongeCodec = SimpleCookingSerializerMixin.impl$buildCodec(SpongeBlastingRecipe::new, defaultCookingTime); - return Codec.mapEither(spongeCodec, mcMapCodec).xmap(to -> to.map(si -> (T) si, i -> i), - fr -> { - if (fr instanceof SpongeBlastingRecipe si) { - return Either.left(si); - } - return Either.right(fr); - }); - } - - if (constructed instanceof CampfireCookingRecipe) { - var spongeCodec = SimpleCookingSerializerMixin.impl$buildCodec(SpongeCampfireCookingRecipe::new, defaultCookingTime); - return Codec.mapEither(spongeCodec, mcMapCodec).xmap(to -> to.map(si -> (T) si, i -> i), - fr -> { - if (fr instanceof SpongeCampfireCookingRecipe si) { - return Either.left(si); - } - return Either.right(fr); - }); - } - - - if (constructed instanceof SmeltingRecipe) { - var spongeCodec = SimpleCookingSerializerMixin.impl$buildCodec(SpongeSmeltingRecipe::new, defaultCookingTime); - return Codec.mapEither(spongeCodec, mcMapCodec).xmap(to -> to.map(si -> (T) si, i -> i), - fr -> { - if (fr instanceof SpongeSmeltingRecipe si) { - return Either.left(si); - } - return Either.right(fr); - }); - } - - - - if (constructed instanceof SmokingRecipe) { - var spongeCodec = SimpleCookingSerializerMixin.impl$buildCodec(SpongeSmokingRecipe::new, defaultCookingTime); - return Codec.mapEither(spongeCodec, mcMapCodec).xmap(to -> to.map(si -> (T) si, i -> i), - fr -> { - if (fr instanceof SpongeSmokingRecipe si) { - return Either.left(si); - } - return Either.right(fr); - }); - } - - // TODO no sponge handling? - return mcMapCodec; - } - - private static MapCodec impl$buildCodec(CookingRecipeFactory factory, int defaultCookingTime) - { - return RecordCodecBuilder.mapCodec( - $$2 -> $$2.group( - Codec.STRING.fieldOf(SPONGE_TYPE).forGetter(a -> "custom"), - Codec.STRING.optionalFieldOf("group", "").forGetter(AbstractCookingRecipe::getGroup), - CookingBookCategory.CODEC.fieldOf("category").orElse(CookingBookCategory.MISC).forGetter(AbstractCookingRecipe::category), - Ingredient.CODEC_NONEMPTY.fieldOf(Constants.Recipe.COOKING_INGREDIENT).forGetter($$0x -> $$0x.getIngredients().get(0)), - BuiltInRegistries.ITEM.byNameCodec().xmap(ItemStack::new, ItemStack::getItem).fieldOf(Constants.Recipe.RESULT).forGetter($$0x -> ((RecipeResultBridge)$$0x).bridge$result()), - Codec.FLOAT.fieldOf(Constants.Recipe.COOKING_EXP).orElse(0.0F).forGetter(AbstractCookingRecipe::getExperience), - Codec.INT.fieldOf(Constants.Recipe.COOKING_TIME).orElse(defaultCookingTime).forGetter(AbstractCookingRecipe::getCookingTime), - ItemStack.CODEC.optionalFieldOf(Constants.Recipe.SPONGE_RESULT, ItemStack.EMPTY).forGetter($$0x -> ((RecipeResultBridge)$$0x).bridge$spongeResult()), - Codec.STRING.optionalFieldOf(Constants.Recipe.SPONGE_RESULTFUNCTION).forGetter(ResultFunctionRecipe::resultFunctionId) - ) - .apply($$2, factory::create) - ); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SimpleCraftingRecipeSerializerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SimpleCraftingRecipeSerializerMixin.java deleted file mode 100644 index 9defd9d4e6b..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SimpleCraftingRecipeSerializerMixin.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.item.crafting; - -import com.mojang.datafixers.kinds.App; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.world.item.crafting.CraftingRecipe; -import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.item.recipe.crafting.custom.SpongeSpecialRecipe; - -import java.util.function.Function; - -@Mixin(SimpleCraftingRecipeSerializer.class) -public abstract class SimpleCraftingRecipeSerializerMixin { - - - @Redirect(method = "", at = @At(value = "INVOKE", - target = "Lcom/mojang/serialization/codecs/RecordCodecBuilder;mapCodec(Ljava/util/function/Function;)Lcom/mojang/serialization/MapCodec;")) - private MapCodec impl$onCreateCodec(final Function, ? extends App, T>> builder) { - final var mcMapCodec = RecordCodecBuilder.mapCodec(builder); - final var eitherMapCodec = Codec.mapEither((MapCodec) SpongeSpecialRecipe.SPONGE_CODEC, mcMapCodec); - return eitherMapCodec.xmap(to -> to.map(si -> si, i -> i), fr -> { - if (fr instanceof SpongeSpecialRecipe) { - return Either.left(fr); - } - return Either.right(fr); - }); - } - -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SingleItemRecipeMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SingleItemRecipeMixin.java index e53ce088d5a..72278d3a397 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SingleItemRecipeMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SingleItemRecipeMixin.java @@ -24,7 +24,10 @@ */ package org.spongepowered.common.mixin.core.world.item.crafting; +import net.minecraft.core.component.DataComponentPatch; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; +import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.SingleItemRecipe; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -38,13 +41,13 @@ public abstract class SingleItemRecipeMixin implements RecipeResultBridge { // @formatter=off - @Shadow @Final protected ItemStack result; - + @Shadow @Final private ItemStackTemplate result; + @Shadow public abstract Ingredient shadow$input(); // @formatter=on @Override public ItemStack bridge$result() { - return this.result; + return this.result.apply(DataComponentPatch.EMPTY); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SingleItemRecipe_SerializerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SingleItemRecipe_SerializerMixin.java deleted file mode 100644 index 251b5bb9d9d..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SingleItemRecipe_SerializerMixin.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.item.crafting; - -import com.mojang.datafixers.kinds.App; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.world.item.crafting.SingleItemRecipe; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.item.recipe.stonecutting.SpongeStonecuttingRecipe; - -import java.util.function.Function; - -@Mixin(SingleItemRecipe.Serializer.class) -public abstract class SingleItemRecipe_SerializerMixin { - - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/codecs/RecordCodecBuilder;mapCodec(Ljava/util/function/Function;)Lcom/mojang/serialization/MapCodec;")) - private MapCodec impl$onCreateCodec(final Function, ? extends App, SingleItemRecipe>> builder) { - final var mcMapCodec = RecordCodecBuilder.mapCodec(builder); - return Codec.mapEither(SpongeStonecuttingRecipe.SPONGE_CODEC, mcMapCodec).xmap(to -> to.map(si -> si, i -> i), - fr -> { - if (fr instanceof SpongeStonecuttingRecipe si) { - return Either.left(si); - } - return Either.right(fr); - }); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SmithingTransformRecipeMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SmithingTransformRecipeMixin.java index 531399e070c..4cb4f374159 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SmithingTransformRecipeMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SmithingTransformRecipeMixin.java @@ -25,6 +25,7 @@ package org.spongepowered.common.mixin.core.world.item.crafting; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.SmithingTransformRecipe; import org.spongepowered.asm.mixin.Final; @@ -32,18 +33,20 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.bridge.world.item.crafting.SmithingRecipeBridge; +import java.util.Optional; + @Mixin(SmithingTransformRecipe.class) public abstract class SmithingTransformRecipeMixin implements SmithingRecipeBridge { // @formatter=off - @Shadow @Final Ingredient template; - @Shadow @Final Ingredient base; - @Shadow @Final Ingredient addition; - @Shadow @Final ItemStack result; + @Shadow @Final private Optional template; + @Shadow @Final private Ingredient base; + @Shadow @Final private Optional addition; + @Shadow @Final private ItemStackTemplate result; // @formatter=on @Override - public Ingredient bridge$template() { + public Optional bridge$template() { return this.template; } @@ -53,13 +56,13 @@ public abstract class SmithingTransformRecipeMixin implements SmithingRecipeBrid } @Override - public Ingredient bridge$addition() { + public Optional bridge$addition() { return this.addition; } @Override public ItemStack bridge$result() { - return this.result; + return new ItemStack(this.result.item(), this.result.count(), this.result.components()); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SmithingTransformRecipe_SerializerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SmithingTransformRecipe_SerializerMixin.java deleted file mode 100644 index fd37c712862..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/crafting/SmithingTransformRecipe_SerializerMixin.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.item.crafting; - -import com.mojang.datafixers.kinds.App; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.world.item.crafting.SmithingTransformRecipe; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.item.recipe.smithing.SpongeSmithingRecipe; - -import java.util.function.Function; - -@Mixin(SmithingTransformRecipe.Serializer.class) -public abstract class SmithingTransformRecipe_SerializerMixin { - - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/codecs/RecordCodecBuilder;mapCodec(Ljava/util/function/Function;)Lcom/mojang/serialization/MapCodec;")) - private static MapCodec impl$onCreateCodec(final Function, ? extends App, SmithingTransformRecipe>> builder) { - final var mcMapCodec = RecordCodecBuilder.mapCodec(builder); - return Codec.mapEither(SpongeSmithingRecipe.SPONGE_CODEC, mcMapCodec).xmap(to -> to.map(si -> si, i -> i), - fr -> { - if (fr instanceof SpongeSmithingRecipe si) { - return Either.left(si); - } - return Either.right(fr); - }); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/enchantment/EnchantmentMixin_Attack.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/enchantment/EnchantmentMixin_Attack.java new file mode 100644 index 00000000000..95dd69cfd9b --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/enchantment/EnchantmentMixin_Attack.java @@ -0,0 +1,68 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.item.enchantment; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.ConditionalEffect; +import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraft.world.item.enchantment.effects.EnchantmentValueEffect; +import org.apache.commons.lang3.mutable.MutableFloat; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.event.cause.entity.damage.SpongeAttackTracker; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageStep; +import org.spongepowered.common.item.util.ItemStackUtil; + +import java.util.List; + +@Mixin(Enchantment.class) +public abstract class EnchantmentMixin_Attack { + + @WrapOperation(method = "modifyDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/enchantment/Enchantment;modifyDamageFilteredValue(Lnet/minecraft/core/component/DataComponentType;Lnet/minecraft/server/level/ServerLevel;ILnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;Lorg/apache/commons/lang3/mutable/MutableFloat;)V")) + private void attack$modifyWeaponEnchantment( + final Enchantment self, final DataComponentType>> component, + final ServerLevel level, final int enchantmentLevel, final ItemStack weapon, final Entity target, final DamageSource source, final MutableFloat damage, final Operation operation) { + + final SpongeAttackTracker tracker = SpongeAttackTracker.of(source); + if (tracker == null) { + operation.call(self, component, level, enchantmentLevel, weapon, target, source, damage); + return; + } + + final SpongeDamageStep step = tracker.newStep(DamageStepTypes.WEAPON_ENCHANTMENT, ItemStackUtil.snapshotOf(weapon), self); + damage.setValue((float) step.applyChildrenBefore(damage.floatValue())); + if (!step.isSkipped()) { + operation.call(self, component, level, enchantmentLevel, weapon, target, source, damage); + } + damage.setValue((float) step.applyChildrenAfter(damage.floatValue())); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/ExplosionMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/ExplosionMixin.java deleted file mode 100644 index ce2d4955c35..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/ExplosionMixin.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.level; - -import com.llamalad7.mixinextras.expression.Expression; -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.ExplosionDamageCalculator; -import net.minecraft.world.level.Level; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; -import org.objectweb.asm.Opcodes; -import org.spongepowered.api.event.Cause; -import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.world.ExplosionEvent; -import org.spongepowered.api.world.explosion.Explosion; -import org.spongepowered.api.world.server.ServerLocation; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.asm.mixin.injection.Surrogate; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.bridge.world.level.ExplosionBridge; -import org.spongepowered.common.event.ShouldFire; -import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.util.VecHelper; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.StringJoiner; - -@Mixin(net.minecraft.world.level.Explosion.class) -public abstract class ExplosionMixin implements ExplosionBridge { - - // @formatter:off - @Shadow @Final private ObjectArrayList toBlow; - @Shadow @Final private Map hitPlayers; - @Shadow @Final private boolean fire; - @Shadow @Final private net.minecraft.world.level.Level level; - @Shadow @Final private double x; - @Shadow @Final private double y; - @Shadow @Final private double z; - @Shadow @Final private Entity source; - @Shadow @Final private float radius; - @Shadow @Final private net.minecraft.world.level.Explosion.BlockInteraction blockInteraction; - // @formatter:on - - private boolean impl$shouldBreakBlocks; - private boolean impl$shouldDamageEntities; - private boolean impl$shouldPlaySmoke; - private int impl$resolution; - private float impl$randomness; - private double impl$knockback; - - @Inject( - method = "(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;Lnet/minecraft/world/level/ExplosionDamageCalculator;DDDFZLnet/minecraft/world/level/Explosion$BlockInteraction;Lnet/minecraft/core/particles/ParticleOptions;Lnet/minecraft/core/particles/ParticleOptions;Lnet/minecraft/core/Holder;)V", - at = @At("RETURN") - ) - private void impl$onConstructed(final Level $$0, final Entity $$1, final DamageSource $$2, final ExplosionDamageCalculator $$3, final double $$4, - final double $$5, final double $$6, final float $$7, final boolean $$8, final net.minecraft.world.level.Explosion.BlockInteraction $$9, - final ParticleOptions $$10, final ParticleOptions $$11, final Holder $$12, final CallbackInfo ci) { - // In Vanilla and Forge, 'damagesTerrain' controls both smoke particles and block damage - // Sponge-created explosions will explicitly set 'impl$shouldBreakBlocks' to its proper value - this.impl$shouldBreakBlocks = this.blockInteraction == net.minecraft.world.level.Explosion.BlockInteraction.DESTROY_WITH_DECAY || this.blockInteraction == net.minecraft.world.level.Explosion.BlockInteraction.DESTROY; - this.impl$shouldDamageEntities = true; - this.impl$resolution = 16; - this.impl$randomness = 1.0F; - this.impl$knockback = 1.0; - } - - // (Lnet/minecraft/world/World;Lnet/minecraft/entity/Entity;Lnet/minecraft/util/DamageSource;Lnet/minecraft/world/ExplosionContext;DDDFZLnet/minecraft/world/Explosion$Mode;Lorg/spongepowered/asm/mixin/injection/callback/CallbackInfo;)V - @Surrogate - private void impl$onConstructed(final Level worldIn, final Entity exploderIn, final DamageSource damageSourceIn, final ExplosionDamageCalculator explosionContextIn, final double xIn, final double yIn, final double zIn, final float sizeIn, final boolean causesFireIn, final net.minecraft.world.level.Explosion.BlockInteraction modeIn, final CallbackInfo ci) { - // In Vanilla and Forge, 'damagesTerrain' controls both smoke particles and block damage - // Sponge-created explosions will explicitly set 'impl$shouldBreakBlocks' to its proper value - this.impl$shouldBreakBlocks = this.blockInteraction == net.minecraft.world.level.Explosion.BlockInteraction.DESTROY_WITH_DECAY || this.blockInteraction == net.minecraft.world.level.Explosion.BlockInteraction.DESTROY; - this.impl$shouldDamageEntities = true; - this.impl$resolution = 16; - this.impl$randomness = 1.0F; - this.impl$knockback = 1.0; - } - - @Inject(method = "explode", at = @At("HEAD"), cancellable = true) - private void impl$onExplodeStart(final CallbackInfo ci) { - if (this.level.isClientSide) { - ci.cancel(); - } - } - - @Expression("? < 16") - @ModifyExpressionValue(method = "explode", - at = @At(value = "MIXINEXTRAS:EXPRESSION", ordinal = 0), - slice = @Slice( - from = @At(value = "FIELD", target = "Lnet/minecraft/world/level/gameevent/GameEvent;EXPLODE:Lnet/minecraft/core/Holder$Reference;", opcode = Opcodes.GETSTATIC), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/ExplosionDamageCalculator;getBlockExplosionResistance(Lnet/minecraft/world/level/Explosion;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/material/FluidState;)Ljava/util/Optional;"))) - private boolean impl$shouldIterateBlocks(final boolean original) { - return this.impl$shouldBreakBlocks && original; - } - - @WrapOperation(method = "explode", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;")) - private List impl$filterEntitiesAndFireEvent(final Level level, final Entity source, final AABB aabb, final Operation> original) { - final List list = this.impl$shouldDamageEntities ? original.call(level, source, aabb) : Collections.emptyList(); - - if (ShouldFire.EXPLOSION_EVENT_DETONATE) { - final List blockPositions = new ArrayList<>(this.toBlow.size()); - final List entities = new ArrayList<>(list.size()); - for (final BlockPos pos : this.toBlow) { - blockPositions - .add(ServerLocation.of((org.spongepowered.api.world.server.ServerWorld) this.level, pos.getX(), pos.getY(), pos.getZ())); - } - for (final Entity entity : list) { - // Make sure to check the entity is immune first. - if (!entity.ignoreExplosion((net.minecraft.world.level.Explosion) (Object) this)) { - entities.add((org.spongepowered.api.entity.Entity) entity); - } - } - final Cause cause = PhaseTracker.getInstance().currentCause(); - final ExplosionEvent.Detonate detonate = SpongeEventFactory.createExplosionEventDetonate(cause, blockPositions, entities, - (Explosion) this, (org.spongepowered.api.world.server.ServerWorld) this.level); - SpongeCommon.post(detonate); - // Clear the positions so that they can be pulled from the event - this.toBlow.clear(); - if (detonate.isCancelled()) { - return List.of(); - } - if (this.impl$shouldBreakBlocks) { - for (final ServerLocation worldLocation : detonate.affectedLocations()) { - this.toBlow.add(VecHelper.toBlockPos(worldLocation)); - } - } - // Clear the list of entities so they can be pulled from the event. - list.clear(); - if (this.impl$shouldDamageEntities) { - for (final org.spongepowered.api.entity.Entity entity : detonate.entities()) { - try { - list.add((Entity) entity); - } catch (final Exception e) { - // Do nothing, a plugin tried to use the wrong entity somehow. - } - } - } - } - - return list; - } - - @ModifyExpressionValue(method = "explode", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/ExplosionDamageCalculator;getKnockbackMultiplier(Lnet/minecraft/world/entity/Entity;)F")) - private float impl$multiplyKnockback(final float original) { - return (float) (original * this.impl$knockback); - } - - @Override - public boolean bridge$getShouldDamageBlocks() { - return this.impl$shouldBreakBlocks; - } - - @Override - public boolean bridge$getShouldDamageEntities() { - return this.impl$shouldDamageEntities; - } - - @Override - public void bridge$setShouldBreakBlocks(final boolean shouldBreakBlocks) { - this.impl$shouldBreakBlocks = shouldBreakBlocks; - } - - @Override - public void bridge$setShouldDamageEntities(final boolean shouldDamageEntities) { - this.impl$shouldDamageEntities = shouldDamageEntities; - } - - @Override - public void bridge$setResolution(final int resolution) { - this.impl$resolution = resolution; - } - - @Override - public int bridge$getResolution() { - return this.impl$resolution; - } - - @Override - public void bridge$setShouldPlaySmoke(final boolean shouldPlaySmoke) { - this.impl$shouldPlaySmoke = shouldPlaySmoke; - } - - @Override - public boolean bridge$getShouldPlaySmoke() { - return this.impl$shouldPlaySmoke; - } - - @Override - public void bridge$setRandomness(final float randomness) { - this.impl$randomness = randomness; - } - - @Override - public float bridge$getRandomness() { - return this.impl$randomness; - } - - @Override - public void bridge$setKnockback(final double knockback) { - this.impl$knockback = knockback; - } - - @Override - public double bridge$getKnockback() { - return this.impl$knockback; - } - - @Override - public String toString() { - return new StringJoiner(", ", ExplosionMixin.class.getSimpleName() + "[", "]") - .add("causesFire=" + this.fire) - .add("mode=" + this.blockInteraction) - .add("world=" + this.level) - .add("x=" + this.x) - .add("y=" + this.y) - .add("z=" + this.z) - .add("exploder=" + this.source) - .add("size=" + this.radius) - .add("affectedBlockPositions=" + this.toBlow) - .add("playerKnockbackMap=" + this.hitPlayers) - .add("shouldBreakBlocks=" + this.impl$shouldBreakBlocks) - .add("shouldDamageEntities=" + this.impl$shouldDamageEntities) - .add("resolution=" + this.impl$resolution) - .add("randomness=" + this.impl$randomness) - .add("knockback=" + this.impl$knockback) - .toString(); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/Explosion_BlockInteractionMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/Explosion_BlockInteractionMixin.java new file mode 100644 index 00000000000..dd00e4d8c1a --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/Explosion_BlockInteractionMixin.java @@ -0,0 +1,34 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.level; + +import net.minecraft.world.level.Explosion; +import org.spongepowered.api.world.explosion.ExplosionBlockInteraction; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(Explosion.BlockInteraction.class) +public interface Explosion_BlockInteractionMixin extends ExplosionBlockInteraction { + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/LevelMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/LevelMixin.java index 99c4529f9c4..d01b0545cd0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/LevelMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/LevelMixin.java @@ -26,36 +26,23 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; -import net.minecraft.core.registries.Registries; +import net.minecraft.core.RegistryAccess; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.ProblemReporter; import net.minecraft.util.RandomSource; -import net.minecraft.world.DifficultyInstance; -import net.minecraft.world.entity.LightningBolt; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.decoration.ArmorStand; -import net.minecraft.world.entity.decoration.HangingEntity; -import net.minecraft.world.entity.decoration.Painting; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.projectile.ThrownEnderpearl; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.TickingBlockEntity; -import net.minecraft.world.level.border.WorldBorder; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.level.storage.TagValueInput; import net.minecraft.world.level.storage.WritableLevelData; import net.minecraft.world.phys.AABB; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.data.persistence.DataView; import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.entity.EntityType; -import org.spongepowered.api.entity.projectile.EnderPearl; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; @@ -64,14 +51,10 @@ import org.spongepowered.asm.mixin.injection.Coerce; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.accessor.world.entity.MobAccessor; -import org.spongepowered.common.accessor.world.entity.item.FallingBlockEntityAccessor; import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.data.persistence.NBTTranslator; import org.spongepowered.common.entity.EntityDataUtil; -import org.spongepowered.common.entity.projectile.UnknownProjectileSource; import org.spongepowered.common.util.Constants; import org.spongepowered.common.util.DataUtil; import org.spongepowered.math.vector.Vector3d; @@ -97,14 +80,14 @@ public abstract class LevelMixin implements LevelBridge, LevelAccessor { @Shadow public abstract net.minecraft.resources.ResourceKey shadow$dimension(); @Shadow public abstract DimensionType shadow$dimensionType(); @Shadow public abstract LevelChunk shadow$getChunkAt(BlockPos p_175726_1_); - @Shadow public abstract DifficultyInstance shadow$getCurrentDifficultyAt(BlockPos p_175649_1_); @Shadow public abstract boolean shadow$isRaining(); @Shadow public abstract net.minecraft.world.level.block.entity.@Nullable BlockEntity shadow$getBlockEntity(BlockPos p_175625_1_); - @Shadow public abstract WorldBorder shadow$getWorldBorder(); + @Shadow public abstract RegistryAccess shadow$registryAccess(); //@Shadow protected abstract void shadow$postGameEventInRadius(@javax.annotation.Nullable net.minecraft.world.entity.Entity $$0, GameEvent $$1, BlockPos $$2, int $$3); // @formatter on + @Override public boolean bridge$isFake() { return this.isClientSide(); @@ -157,11 +140,14 @@ public abstract class LevelMixin implements LevelBridge, LevelAccessor { final CompoundTag entityData = updatedContainer.getView(Constants.Entity.V2.DATA) .map(NBTTranslator.INSTANCE::translate) .orElseThrow(() -> new IllegalArgumentException("Missing entity data!")); + // mimicing Entity#restoreFrom entityData.remove("Dimension"); final Entity createdEntity = this.bridge$createEntity(EntityDataUtil.entityType(entityData), proposedPosition, false); - ((net.minecraft.world.entity.Entity) createdEntity).load(entityData); - ((net.minecraft.world.entity.Entity) createdEntity).moveTo(proposedPosition.x(), proposedPosition.y(), proposedPosition.z()); + final var e = (net.minecraft.world.entity.Entity) createdEntity; + e.load(TagValueInput.create(ProblemReporter.DISCARDING, this.shadow$registryAccess(), entityData)); + // position needs a reset + e.snapTo(proposedPosition.x(), proposedPosition.y(), proposedPosition.z()); if (rotation != null) { createdEntity.setRotation(rotation); @@ -173,74 +159,6 @@ public abstract class LevelMixin implements LevelBridge, LevelAccessor { return (E) createdEntity; } - @Override - @SuppressWarnings("unchecked") - public E bridge$createEntity(final EntityType type, final Vector3d position, final boolean naturally) throws IllegalArgumentException, IllegalStateException { - if (type == net.minecraft.world.entity.EntityType.PLAYER) { - // Unable to construct these - throw new IllegalArgumentException("A Player cannot be created by the API!"); - } - - net.minecraft.world.entity.Entity entity = null; - final double x = position.x(); - final double y = position.y(); - final double z = position.z(); - final net.minecraft.world.level.Level thisWorld = (net.minecraft.world.level.Level) (Object) this; - // Not all entities have a single World parameter as their constructor - if (type == net.minecraft.world.entity.EntityType.LIGHTNING_BOLT) { - entity = net.minecraft.world.entity.EntityType.LIGHTNING_BOLT.create(thisWorld); - entity.moveTo(x, y, z); - ((LightningBolt) entity).setVisualOnly(false); - } - // TODO - archetypes should solve the problem of calling the correct constructor - if (type == net.minecraft.world.entity.EntityType.ENDER_PEARL) { - final ArmorStand tempEntity = new ArmorStand(thisWorld, x, y, z); - tempEntity.setPos(tempEntity.getX(), tempEntity.getY() - tempEntity.getEyeHeight(), tempEntity.getZ()); - entity = new ThrownEnderpearl(thisWorld, tempEntity); - ((EnderPearl) entity).offer(Keys.SHOOTER, UnknownProjectileSource.UNKNOWN); - } - // Some entities need to have non-null fields (and the easiest way to - // set them is to use the more specialised constructor). - if (type == net.minecraft.world.entity.EntityType.FALLING_BLOCK) { - entity = FallingBlockEntityAccessor.invoker$new(thisWorld, x, y, z, Blocks.SAND.defaultBlockState()); - } - if (type == net.minecraft.world.entity.EntityType.ITEM) { - entity = new ItemEntity(thisWorld, x, y, z, new ItemStack(Blocks.STONE)); - } - - if (entity == null) { - final ResourceKey key = (ResourceKey) (Object) SpongeCommon.vanillaRegistry(Registries.ENTITY_TYPE).getKey((net.minecraft.world.entity.EntityType) type); - try { - entity = ((net.minecraft.world.entity.EntityType) type).create(thisWorld); - entity.moveTo(x, y, z); - } catch (final Exception e) { - throw new RuntimeException("There was an issue attempting to construct " + key, e); - } - } - - // TODO - replace this with an actual check - - if (entity instanceof HangingEntity) { - if (!((HangingEntity) entity).survives()) { - throw new IllegalArgumentException("Hanging entity does not survive at the given position: " + position); - } - } - - if (naturally && entity instanceof Mob) { - // Adding the default equipment - final DifficultyInstance difficulty = this.shadow$getCurrentDifficultyAt(BlockPos.containing(x, y, z)); - ((MobAccessor) entity).invoker$populateDefaultEquipmentSlots(this.random, difficulty); - } - - if (entity instanceof Painting) { - // This is default when art is null when reading from NBT, could - // choose a random art instead? - // TODO ? ((Painting) entity).motive = Motive.KEBAB; - } - - return (E) entity; - } - @Inject(method = { "getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;", "getEntities(Lnet/minecraft/world/level/entity/EntityTypeTest;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;", diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/NaturalSpawnerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/NaturalSpawnerMixin.java index fa1f306f5a3..d72be63615e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/NaturalSpawnerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/NaturalSpawnerMixin.java @@ -40,7 +40,7 @@ @Mixin(NaturalSpawner.class) public abstract class NaturalSpawnerMixin { - @WrapOperation(method = "spawnForChunk", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/NaturalSpawner$SpawnState;canSpawnForCategory(Lnet/minecraft/world/entity/MobCategory;Lnet/minecraft/world/level/ChunkPos;)Z")) + @WrapOperation(method = "spawnForChunk", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/NaturalSpawner$SpawnState;canSpawnForCategoryLocal(Lnet/minecraft/world/entity/MobCategory;Lnet/minecraft/world/level/ChunkPos;)Z")) private static boolean impl$spawnTickRate(final NaturalSpawner.SpawnState state, final MobCategory category, final ChunkPos pos, final Operation original, @Local(argsOnly = true) final ServerLevel level) { final int tickRate = NaturalSpawnerMixin.impl$getSpawningTickRate(category, level); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/ServerExplosionMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/ServerExplosionMixin.java new file mode 100644 index 00000000000..9572b7d8276 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/ServerExplosionMixin.java @@ -0,0 +1,180 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.level; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.ExplosionDamageCalculator; +import net.minecraft.world.level.ServerExplosion; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.bridge.world.level.ExplosionBridge; +import org.spongepowered.common.event.ShouldFire; + +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; + +@Mixin(net.minecraft.world.level.ServerExplosion.class) +public abstract class ServerExplosionMixin implements ExplosionBridge { + + // @formatter:off + @Shadow @Final private Map hitPlayers; + @Shadow @Final private boolean fire; + @Shadow @Final private ServerLevel level; + @Shadow @Final private Entity source; + @Shadow @Final private float radius; + @Shadow @Final private Vec3 center; + @Shadow @Final private net.minecraft.world.level.Explosion.BlockInteraction blockInteraction; + + @Shadow protected abstract void shadow$interactWithBlocks(final List $$0); + @Shadow protected abstract List shadow$calculateExplodedPositions(); + @Shadow protected abstract boolean shadow$interactsWithBlocks(); + + @Shadow protected abstract void shadow$createFire(final List $$0); + + // @formatter:on + + + private boolean impl$shouldDamageEntities; + private boolean impl$shouldPlaySmoke; + private int impl$resolution; + private float impl$randomness; + private double impl$knockbackMultiplier; + private List impl$affectedBlocks; + + @Inject(method = "", at = @At("RETURN")) + private void impl$onConstructed(final ServerLevel $$0, final Entity $$1, final DamageSource $$2, + final ExplosionDamageCalculator $$3, final Vec3 $$4, final float $$5, final boolean $$6, + final net.minecraft.world.level.Explosion.BlockInteraction $$7, final CallbackInfo ci) { + this.impl$shouldDamageEntities = true; + this.impl$resolution = 16; + this.impl$randomness = 1.0F; + this.impl$knockbackMultiplier = 1.0; + } + + + @Redirect(method = "explode", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/ServerExplosion;calculateExplodedPositions()Ljava/util/List;")) + private List impl$onCalculateExplodedPositions(final ServerExplosion instance) { + // If we don't fire the event and do not affect any blocks we can ignore the calculation + if (!this.fire && !this.shadow$interactsWithBlocks() && !ShouldFire.EXPLOSION_EVENT_DETONATE) { + return List.of(); + } + + this.impl$affectedBlocks = this.shadow$calculateExplodedPositions(); + return this.impl$affectedBlocks; + } + + @Redirect(method = "explode", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/ServerExplosion;interactWithBlocks(Ljava/util/List;)V")) + private void impl$onInteractWithBlocks(final ServerExplosion instance, final List $$0) { + this.shadow$interactWithBlocks(this.impl$affectedBlocks); + } + + @Redirect(method = "explode", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/ServerExplosion;createFire(Ljava/util/List;)V")) + private void impl$onCreateFire(final ServerExplosion instance, final List $$0) { + this.shadow$createFire(this.impl$affectedBlocks); + } + + @Override + public boolean bridge$getShouldDamageEntities() { + return this.impl$shouldDamageEntities; + } + + @Override + public void bridge$setShouldDamageEntities(final boolean shouldDamageEntities) { + this.impl$shouldDamageEntities = shouldDamageEntities; + } + + @Override + public void bridge$setResolution(final int resolution) { + this.impl$resolution = resolution; + } + + @Override + public int bridge$getResolution() { + return this.impl$resolution; + } + + @Override + public void bridge$setShouldPlaySmoke(final boolean shouldPlaySmoke) { + this.impl$shouldPlaySmoke = shouldPlaySmoke; + } + + @Override + public boolean bridge$getShouldPlaySmoke() { + return this.impl$shouldPlaySmoke; + } + + @Override + public void bridge$setRandomness(final float randomness) { + this.impl$randomness = randomness; + } + + @Override + public float bridge$getRandomness() { + return this.impl$randomness; + } + + @Override + public void bridge$setKnockback(final double knockback) { + this.impl$knockbackMultiplier = knockback; + } + + @Override + public double bridge$getKnockback() { + return this.impl$knockbackMultiplier; + } + + @Override + public String toString() { + return new StringJoiner(", ", ServerExplosionMixin.class.getSimpleName() + "[", "]") + .add("causesFire=" + this.fire) + .add("blockInteraction=" + this.blockInteraction) + .add("world=" + this.level) + .add("x=" + this.center.x) + .add("y=" + this.center.y) + .add("z=" + this.center.z) + .add("exploder=" + this.source) + .add("size=" + this.radius) + .add("playerKnockbackMap=" + this.hitPlayers) + .add("shouldDamageEntities=" + this.impl$shouldDamageEntities) + .add("resolution=" + this.impl$resolution) + .add("randomness=" + this.impl$randomness) + .add("knockback=" + this.impl$knockbackMultiplier) + .toString(); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/BaseFireBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/BaseFireBlockMixin.java index fa2bc78788e..4c5323300be 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/BaseFireBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/BaseFireBlockMixin.java @@ -24,34 +24,49 @@ */ package org.spongepowered.common.mixin.core.world.level.block; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.core.BlockPos; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.damagesource.DamageSources; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.InsideBlockEffectApplier; +import net.minecraft.world.entity.InsideBlockEffectType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseFireBlock; import net.minecraft.world.level.block.state.BlockState; -import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.accessor.world.DamageSourcesAccessor; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageSources; import org.spongepowered.common.mixin.core.block.BlockMixin; +import java.util.function.Consumer; + @Mixin(BaseFireBlock.class) public abstract class BaseFireBlockMixin extends BlockMixin { - @Redirect(method = "entityInside", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/DamageSources;inFire()Lnet/minecraft/world/damagesource/DamageSource;")) - private DamageSource impl$spongeRedirectForFireDamage(final DamageSources instance, final BlockState blockState, final Level world, final BlockPos blockPos, final Entity entity) { - final DamageSource source = instance.inFire(); - if (world.isClientSide) { // Short Circuit - return source; + @WrapOperation(method = "entityInside", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/InsideBlockEffectApplier;runAfter(Lnet/minecraft/world/entity/InsideBlockEffectType;Ljava/util/function/Consumer;)V" + )) + private void impl$spongeRedirectForFireDamage( + final InsideBlockEffectApplier instance, final InsideBlockEffectType insideBlockEffectType, + final Consumer entityConsumer, final Operation original, final BlockState state, + final Level world, BlockPos pos, Entity entity, InsideBlockEffectApplier sameApplier + ) { + if (world.isClientSide()) { + original.call(instance, insideBlockEffectType, entityConsumer); + return; + } + // We'll be replacing the DamageSources.inFire() with our own temporarily then roll it back... + final var originalInFire = world.damageSources().inFire(); + final var blockSource = SpongeDamageSources.createBlockBasedDamageSource((ServerWorld) world, pos, originalInFire); + try { + ((DamageSourcesAccessor) world.damageSources()).accessor$setInFire(blockSource); + original.call(instance, insideBlockEffectType, entityConsumer); + } finally { + ((DamageSourcesAccessor) world.damageSources()).accessor$setInFire(originalInFire); } - final ServerLocation location = ServerLocation.of((ServerWorld) world, blockPos.getX(), blockPos.getY(), blockPos.getZ()); - var blockSource = org.spongepowered.api.event.cause.entity.damage.source.DamageSource.builder() - .from((org.spongepowered.api.event.cause.entity.damage.source.DamageSource) source).block(location) - .block(location.createSnapshot()).build(); - return (DamageSource) blockSource; } + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/CactusBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/CactusBlockMixin.java index d02dd28bcb5..b0ae94cdbf6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/CactusBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/CactusBlockMixin.java @@ -31,27 +31,27 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.CactusBlock; import net.minecraft.world.level.block.state.BlockState; -import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageSources; import org.spongepowered.common.mixin.core.block.BlockMixin; @Mixin(CactusBlock.class) public abstract class CactusBlockMixin extends BlockMixin { @Redirect(method = "entityInside", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/DamageSources;cactus()Lnet/minecraft/world/damagesource/DamageSource;")) - private DamageSource impl$reAssignForBlockDamageSource(final DamageSources instance, final BlockState $$0, final Level world, final BlockPos pos, final Entity $$3) { + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/damagesource/DamageSources;cactus()Lnet/minecraft/world/damagesource/DamageSource;" + )) + private DamageSource impl$reAssignForBlockDamageSource( + final DamageSources instance, final BlockState $$0, final Level world, + final BlockPos pos, final Entity $$3) { final DamageSource source = instance.cactus(); if (world.isClientSide()) { return source; } - final ServerLocation location = ServerLocation.of((ServerWorld) world, pos.getX(), pos.getY(), pos.getZ()); - var blockSource = org.spongepowered.api.event.cause.entity.damage.source.DamageSource.builder() - .from((org.spongepowered.api.event.cause.entity.damage.source.DamageSource) source).block(location) - .block(location.createSnapshot()).build(); - return (DamageSource) blockSource; + return SpongeDamageSources.createBlockBasedDamageSource((ServerWorld) world, pos, source); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/CampfireBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/CampfireBlockMixin.java index 8837646fa7d..4db918c5136 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/CampfireBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/CampfireBlockMixin.java @@ -28,18 +28,15 @@ import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageSources; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.CampfireBlock; -import net.minecraft.world.level.block.entity.CampfireBlockEntity; import net.minecraft.world.level.block.state.BlockState; +import org.spongepowered.api.event.cause.entity.damage.DamageTypes; import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.bridge.world.level.block.entity.CampfireBlockEntityBridge; import org.spongepowered.common.mixin.core.block.BlockMixin; @Mixin(CampfireBlock.class) @@ -50,23 +47,15 @@ public abstract class CampfireBlockMixin extends BlockMixin { at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/DamageSources;campfire()Lnet/minecraft/world/damagesource/DamageSource;")) private DamageSource impl$spongeRedirectForFireDamage(final DamageSources instance, final BlockState blockState, final Level world, final BlockPos blockPos, final Entity entity) { final DamageSource source = instance.inFire(); - if (world.isClientSide) { // Short Circuit + if (world.isClientSide()) { // Short Circuit return source; } final ServerLocation location = ServerLocation.of((ServerWorld) world, blockPos.getX(), blockPos.getY(), blockPos.getZ()); var blockSource = org.spongepowered.api.event.cause.entity.damage.source.DamageSource.builder() .from((org.spongepowered.api.event.cause.entity.damage.source.DamageSource) source).block(location) + .type(DamageTypes.CAMPFIRE) .block(location.createSnapshot()).build(); return (DamageSource) blockSource; } - @Redirect(method = "useItemOn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/CampfireBlockEntity;placeFood(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/item/ItemStack;I)Z")) - public boolean impl$placeFood(final CampfireBlockEntity instance, final LivingEntity player, final ItemStack item, final int cookingTime) { - if (instance.placeFood(player, item, cookingTime)) { - ((CampfireBlockEntityBridge) instance).bridge$placeRecipe(instance.getCookableRecipe(item).get()); - return true; - } - return false; - } - } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/commands/arguments/ComponentArgumentMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/ColorCollectionMixin.java similarity index 54% rename from src/mixins/java/org/spongepowered/common/mixin/core/commands/arguments/ComponentArgumentMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/ColorCollectionMixin.java index e441118005d..543592a19e3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/commands/arguments/ComponentArgumentMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/ColorCollectionMixin.java @@ -22,33 +22,37 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.commands.arguments; +package org.spongepowered.common.mixin.core.world.level.block; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.StringReader; -import net.minecraft.commands.arguments.ComponentArgument; -import net.minecraft.network.chat.Component; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ColorCollection; +import net.minecraft.world.level.block.state.BlockBehaviour; +import org.apache.commons.lang3.function.TriFunction; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.bridge.block.DyeColorBlockBridge; -@Mixin(ComponentArgument.class) -public abstract class ComponentArgumentMixin { +import java.util.function.BiFunction; +import java.util.function.Function; - @Inject(method = "parse", at = @At("RETURN")) - private void impl$correctForOffByOneErrorInReaderPosition(final StringReader var1, - final CallbackInfoReturnable cir) { - // If we're able to continue reading, the problem we have is that - // the JSON reader has advanced as far as the next element instead. - // We need to step back one, but only if: - // * There is more reading to do - // * The next character is not the argument separator - // * The preceding character is the argument separator - if (var1.canRead() && var1.peek() != CommandDispatcher.ARGUMENT_SEPARATOR_CHAR - && var1.peek(-1) == CommandDispatcher.ARGUMENT_SEPARATOR_CHAR) { - var1.setCursor(var1.getCursor() - 1); +@Mixin(ColorCollection.class) +public abstract class ColorCollectionMixin { + + @Inject(method = "registerBlocks", at = @At("RETURN")) + private static void impl$stampDyeColor( + final ColorCollection ids, + final TriFunction, BlockBehaviour.Properties, Block> register, + final BiFunction colorBlockFactory, + final Function propertiesSupplier, + final CallbackInfoReturnable> cir + ) { + final ColorCollection collection = cir.getReturnValue(); + for (final DyeColor color : DyeColor.values()) { + ((DyeColorBlockBridge) collection.pick(color)) + .bridge$setDyeColor((org.spongepowered.api.data.type.DyeColor) (Object) color); } } - } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/EndPortalBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/EndPortalBlockMixin.java index 74e5aca88c8..944dee0b95f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/EndPortalBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/EndPortalBlockMixin.java @@ -30,7 +30,8 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.EndPortalBlock; import net.minecraft.world.level.levelgen.feature.EndPlatformFeature; -import net.minecraft.world.level.portal.DimensionTransition; +import net.minecraft.world.level.portal.TeleportTransition; +import net.minecraft.world.phys.Vec3; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.util.Axis; import org.spongepowered.api.world.portal.Portal; @@ -66,12 +67,12 @@ public abstract class EndPortalBlockMixin implements PortalBlockBridge { } if (entity instanceof ServerPlayer player) { - var transition = player.findRespawnPositionAndUseSpawnBlock(false, DimensionTransition.DO_NOTHING); - return ServerLocation.of((ServerWorld) transition.newLevel(), VecHelper.toVector3d(transition.pos())); + var transition = player.findRespawnPositionAndUseSpawnBlock(false, TeleportTransition.DO_NOTHING); + return ServerLocation.of((ServerWorld) transition.newLevel(), VecHelper.toVector3d(transition.position())); } - final var sharedSpawnPos = toLevel.getSharedSpawnPos(); - final var spawnPos = ((net.minecraft.world.entity.Entity) entity).adjustSpawnLocation(toLevel, sharedSpawnPos).getBottomCenter(); + final var sharedSpawnPos = toLevel.getRespawnData().pos(); + final var spawnPos = Vec3.atBottomCenterOf(((net.minecraft.world.entity.Entity) entity).adjustSpawnLocation(toLevel, sharedSpawnPos)); return ServerLocation.of((ServerWorld) toLevel, VecHelper.toVector3d(spawnPos)); } @@ -90,7 +91,7 @@ public abstract class EndPortalBlockMixin implements PortalBlockBridge { final var level = (ServerLevel) at.world(); // If a target dimension is set assume we always want to generate a portal otherwise we could have used a spawn teleporter if (level.dimension() == Level.END) { - final var bottomCenter = VecHelper.toBlockPos(at.blockPosition()).getBottomCenter(); + final var bottomCenter = Vec3.atBottomCenterOf(VecHelper.toBlockPos(at.blockPosition())); EndPlatformFeature.createEndPlatform(level, BlockPos.containing(bottomCenter).below(), true); return Optional.of(new SpongePortal(at, (PortalLogic) this)); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/FarmBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/FarmBlockMixin.java index 8a0db18f320..3d1d6fc6559 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/FarmBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/FarmBlockMixin.java @@ -27,7 +27,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.FarmBlock; +import net.minecraft.world.level.block.FarmlandBlock; import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -36,7 +36,7 @@ import org.spongepowered.common.bridge.world.entity.GrieferBridge; import org.spongepowered.common.mixin.core.block.BlockMixin; -@Mixin(FarmBlock.class) +@Mixin(FarmlandBlock.class) public abstract class FarmBlockMixin extends BlockMixin { // @formatter:off @@ -46,7 +46,7 @@ public abstract class FarmBlockMixin extends BlockMixin { @Redirect(method = "fallOn", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/level/block/FarmBlock;turnToDirt(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)V" + target = "Lnet/minecraft/world/level/block/FarmlandBlock;turnToDirt(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)V" ) ) private void impl$CheckIfGrieferCanGrief(Entity entityIn, BlockState state, Level world, BlockPos pos) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/MagmaBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/MagmaBlockMixin.java index 6dd01f2334f..5b496394ec9 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/MagmaBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/MagmaBlockMixin.java @@ -31,11 +31,11 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.MagmaBlock; import net.minecraft.world.level.block.state.BlockState; -import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageSources; import org.spongepowered.common.mixin.core.block.BlockMixin; @Mixin(MagmaBlock.class) @@ -43,15 +43,14 @@ public abstract class MagmaBlockMixin extends BlockMixin { @Redirect(method = "stepOn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/DamageSources;hotFloor()Lnet/minecraft/world/damagesource/DamageSource;")) - private DamageSource impl$spongeRedirectForFireDamage(final DamageSources instance, final Level world, final BlockPos blockPos, final BlockState blockState, final Entity entity) { + private DamageSource impl$spongeRedirectForFireDamage( + final DamageSources instance, final Level world, final BlockPos blockPos, + final BlockState blockState, final Entity entity + ) { final DamageSource source = instance.hotFloor(); - if (world.isClientSide) { // Short Circuit + if (world.isClientSide()) { // Short Circuit return source; } - final ServerLocation location = ServerLocation.of((ServerWorld) world, blockPos.getX(), blockPos.getY(), blockPos.getZ()); - var blockSource = org.spongepowered.api.event.cause.entity.damage.source.DamageSource.builder() - .from((org.spongepowered.api.event.cause.entity.damage.source.DamageSource) source).block(location) - .block(location.createSnapshot()).build(); - return (DamageSource) blockSource; + return SpongeDamageSources.createBlockBasedDamageSource((ServerWorld) world, blockPos, source); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/NetherPortalBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/NetherPortalBlockMixin.java index 8a39171148b..b96a261afa0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/NetherPortalBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/NetherPortalBlockMixin.java @@ -24,11 +24,11 @@ */ package org.spongepowered.common.mixin.core.world.level.block; -import net.minecraft.BlockUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.BlockUtil; import net.minecraft.world.entity.ai.village.poi.PoiManager; import net.minecraft.world.entity.ai.village.poi.PoiRecord; import net.minecraft.world.entity.ai.village.poi.PoiTypes; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/NoteBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/NoteBlockMixin.java index 4fe5b0db4a0..41208509899 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/NoteBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/NoteBlockMixin.java @@ -50,8 +50,11 @@ public abstract class NoteBlockMixin extends BlockMixin { @SuppressWarnings("ConstantConditions") - @Inject(method = "trigge rEvent", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;playSeededSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;FFJ)V"), + @Inject(method = "triggerEvent", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/Level;playSeededSound(Lnet/minecraft/world/entity/Entity;DDDLnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;FFJ)V" + ), cancellable = true) // TODO mixin earlier to cancel tuning particles private void impl$throwNoteBlockSoundEvent(BlockState state, Level worldIn, BlockPos pos, int id, int param, CallbackInfoReturnable callbackInfo) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/PointedDripstoneBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/PointedDripstoneBlockMixin.java index 1b8fb74d752..2edc3d52117 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/PointedDripstoneBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/PointedDripstoneBlockMixin.java @@ -31,11 +31,11 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.PointedDripstoneBlock; import net.minecraft.world.level.block.state.BlockState; -import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageSources; import org.spongepowered.common.mixin.core.block.BlockMixin; @Mixin(PointedDripstoneBlock.class) @@ -45,17 +45,13 @@ public abstract class PointedDripstoneBlockMixin extends BlockMixin { at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/DamageSources;stalagmite()Lnet/minecraft/world/damagesource/DamageSource;")) private DamageSource impl$spongeRedirectForFireDamage( final DamageSources instance, final Level level, final BlockState state, - final BlockPos pos,final Entity ignored, final float distance + final BlockPos pos,final Entity ignored, final double distance ) { final DamageSource source = instance.stalagmite(); - if (level.isClientSide) { // Short Circuit + if (level.isClientSide()) { // Short Circuit return source; } - final ServerLocation location = ServerLocation.of((ServerWorld) level, pos.getX(), pos.getY(), pos.getZ()); - var blockSource = org.spongepowered.api.event.cause.entity.damage.source.DamageSource.builder() - .from((org.spongepowered.api.event.cause.entity.damage.source.DamageSource) source).block(location) - .block(location.createSnapshot()).build(); - return (DamageSource) blockSource; + return SpongeDamageSources.createBlockBasedDamageSource((ServerWorld) level, pos, source); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/SweetBerryBushBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/SweetBerryBushBlockMixin.java index f66127926c0..d528e0f3a79 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/SweetBerryBushBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/SweetBerryBushBlockMixin.java @@ -31,31 +31,27 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.SweetBerryBushBlock; import net.minecraft.world.level.block.state.BlockState; -import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageSources; import org.spongepowered.common.mixin.core.block.BlockMixin; @Mixin(SweetBerryBushBlock.class) public abstract class SweetBerryBushBlockMixin extends BlockMixin { - @Redirect(method = "entityInside(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/Entity;)V", + @Redirect(method = "entityInside", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/DamageSources;sweetBerryBush()Lnet/minecraft/world/damagesource/DamageSource;")) private DamageSource impl$spongeRedirectForFireDamage( final DamageSources instance, final BlockState state, final Level level, final BlockPos pos, final Entity ignored ) { final DamageSource source = instance.sweetBerryBush(); - if (level.isClientSide) { // Short Circuit + if (level.isClientSide()) { // Short Circuit return source; } - final ServerLocation location = ServerLocation.of((ServerWorld) level, pos.getX(), pos.getY(), pos.getZ()); - var blockSource = org.spongepowered.api.event.cause.entity.damage.source.DamageSource.builder() - .from((org.spongepowered.api.event.cause.entity.damage.source.DamageSource) source).block(location) - .block(location.createSnapshot()).build(); - return (DamageSource) blockSource; + return SpongeDamageSources.createBlockBasedDamageSource((ServerWorld) level, pos, source); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/TntBlockMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/TntBlockMixin.java index 4b71d7df7f0..f221c45e402 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/TntBlockMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/TntBlockMixin.java @@ -24,23 +24,26 @@ */ package org.spongepowered.common.mixin.core.world.level.block; +import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.item.PrimedTnt; import net.minecraft.world.level.Explosion; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.TntBlock; import net.minecraft.world.level.block.state.BlockState; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.entity.living.Living; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.cause.entity.damage.DamageTypes; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.common.bridge.explosives.FusedExplosiveBridge; import org.spongepowered.common.bridge.world.entity.item.PrimedTntBridge; @@ -51,21 +54,23 @@ @Mixin(TntBlock.class) public abstract class TntBlockMixin extends BlockMixin { - private boolean primeCancelled; + @Unique + private boolean impl$primeCancelled; + @Unique private boolean impl$onRemove(Level world, BlockPos pos, boolean isMoving) { - final boolean removed = !this.primeCancelled && world.removeBlock(pos, isMoving); - this.primeCancelled = false; + final boolean removed = !this.impl$primeCancelled && world.removeBlock(pos, isMoving); + this.impl$primeCancelled = false; return removed; } - @Inject(method = "explode(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/LivingEntity;)V", + @Inject(method = "prime(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/LivingEntity;)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z"), - locals = LocalCapture.CAPTURE_FAILSOFT, cancellable = true ) - private static void impl$ThrowPrimeAndMaybeCancel(final Level worldIn, final BlockPos pos, - @Nullable final LivingEntity igniter, final CallbackInfo ci, final PrimedTnt tnt) { + private static void impl$ThrowPrimeAndMaybeCancel( + final Level level, final BlockPos pos, final LivingEntity igniter, final CallbackInfoReturnable cir, + @Local final PrimedTnt tnt) { ((PrimedTntBridge) tnt).bridge$setDetonator(igniter); if (ShouldFire.PRIME_EXPLOSIVE_EVENT_PRE) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { @@ -73,7 +78,7 @@ public abstract class TntBlockMixin extends BlockMixin { frame.addContext(EventContextKeys.IGNITER, (Living) igniter); } if (!((FusedExplosiveBridge) tnt).bridge$shouldPrime()) { - ci.cancel(); + cir.setReturnValue(false); } } } @@ -81,12 +86,12 @@ public abstract class TntBlockMixin extends BlockMixin { @Inject( method = "wasExploded", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z"), + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z"), locals = LocalCapture.CAPTURE_FAILSOFT, cancellable = true ) private void impl$CheckIfCanPrimeFromExplosion( - final Level worldIn, final BlockPos pos, final Explosion explosionIn, final CallbackInfo ci, final PrimedTnt tnt) { + final ServerLevel worldIn, final BlockPos pos, final Explosion explosionIn, final CallbackInfo ci, final PrimedTnt tnt) { if (ShouldFire.PRIME_EXPLOSIVE_EVENT_PRE) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.addContext(EventContextKeys.DAMAGE_TYPE, DamageTypes.EXPLOSION); @@ -110,12 +115,12 @@ public abstract class TntBlockMixin extends BlockMixin { return this.impl$onRemove(world, pos, isMoving); } - @Redirect(method = "use", at = @At(value = "INVOKE", + @Redirect(method = "useItemOn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;setBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;I)Z")) private boolean impl$removeActivated(final Level world, final BlockPos pos, final BlockState state, final int flag) { // Called when player manually ignites TNT - final boolean removed = !this.primeCancelled && world.setBlock(pos, state, flag); - this.primeCancelled = false; + final boolean removed = !this.impl$primeCancelled && world.setBlock(pos, state, flag); + this.impl$primeCancelled = false; return removed; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin.java index c6983e7d8b7..c041cffe4b2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin.java @@ -26,12 +26,12 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.AbstractCookingRecipe; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.item.crafting.RecipeManager; import net.minecraft.world.item.crafting.SingleRecipeInput; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; import net.minecraft.world.level.block.state.BlockState; import org.objectweb.asm.Opcodes; @@ -55,6 +55,7 @@ import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.block.entity.AbstractFurnaceBlockEntityBridge; import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.event.tracking.phase.general.RecipeContext; import org.spongepowered.common.item.util.ItemStackUtil; import java.util.Collections; @@ -65,25 +66,35 @@ public abstract class AbstractFurnaceBlockEntityMixin extends BaseContainerBlock // @Formatter:off @Shadow protected NonNullList items; - @Shadow int cookingProgress; - @Shadow int cookingTotalTime; + @Shadow private int cookingTimer; + @Shadow private int cookingTotalTime; // @Formatter:on @Shadow @Final private RecipeManager.CachedCheck quickCheck; // Shrink Fuel - @Redirect(method = "serverTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;shrink(I)V")) - private static void impl$throwFuelEventIfOrShrink(final ItemStack itemStack, final int quantity, final Level var0, final BlockPos var1, final BlockState var2, final AbstractFurnaceBlockEntity entity) { + @Redirect(method = "consumeFuel", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;shrink(I)V")) + private static void impl$throwFuelEventIfOrShrink( + ItemStack instance, int amount, + final NonNullList slots, final ItemStack fuel + ) { final Cause cause = PhaseTracker.getInstance().currentCause(); + // This is injected or switched in as a context by the platform dependent mixins + if (!(PhaseTracker.getInstance().getPhaseContext() instanceof RecipeContext rCtx)) { + instance.shrink(amount); + return; + } + final var entity = rCtx.furnace(); + final var recipe = rCtx.recipe(); + final var recipeID = rCtx.recipeID(); - final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(itemStack); - final ItemStackSnapshot shrinkedFuel = ItemStackUtil.snapshotOf(ItemStackUtil.cloneDefensive(itemStack, itemStack.getCount() - 1)); + final ItemStackSnapshot fuelSnapshot = ItemStackUtil.snapshotOf(fuel); + final ItemStackSnapshot shrinkedFuel = ItemStackUtil.snapshotOf(ItemStackUtil.cloneDefensive(fuel, fuel.getCount() - 1)); - final SlotTransaction transaction = new SlotTransaction(((FurnaceBlockEntity) entity).inventory().slot(1).get(), fuel, shrinkedFuel); - final var recipe = ((AbstractFurnaceBlockEntityMixin) (Object) entity).bridge$getCurrentRecipe(); - final CookingEvent.ConsumeFuel event = SpongeEventFactory.createCookingEventConsumeFuel(cause, (FurnaceBlockEntity) entity, Optional.of(fuel), - recipe.map(r -> (CookingRecipe) r.value()), recipe.map(r -> (ResourceKey) (Object) r.id()), Collections.singletonList(transaction)); + final SlotTransaction transaction = new SlotTransaction(((FurnaceBlockEntity) entity).inventory().slot(1).get(), fuelSnapshot, shrinkedFuel); + final CookingEvent.ConsumeFuel event = SpongeEventFactory.createCookingEventConsumeFuel(cause, (FurnaceBlockEntity) entity, Optional.of(fuelSnapshot), + Optional.of((CookingRecipe) recipe), Optional.of((ResourceKey) (Object) recipeID), Collections.singletonList(transaction)); SpongeCommon.post(event); if (event.isCancelled()) { ((AbstractFurnaceBlockEntityMixin) (Object) entity).cookingTotalTime = 0; @@ -97,26 +108,17 @@ public abstract class AbstractFurnaceBlockEntityMixin extends BaseContainerBlock if (transaction.custom().isPresent()) { ((AbstractFurnaceBlockEntityMixin) (Object) entity).items.set(1, ItemStackUtil.fromSnapshotToNative(transaction.finalReplacement())); } else { // vanilla - itemStack.shrink(quantity); + instance.shrink(amount); } } @Override public Optional> bridge$getCurrentRecipe() { - var recipeInpuit = new SingleRecipeInput(this.items.get(0)); - return this.quickCheck.getRecipeFor(recipeInpuit, this.level); - } - - // Interrupt-Active - e.g. a player removing the currently smelting item - @Inject( - method = "setItem", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;getTotalCookTime(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;)I" - ) - ) - private void impl$interruptSmelt(final CallbackInfo ci) { - this.impl$callInteruptSmeltEvent(); + if (!(this.level instanceof ServerLevel se)) { + return Optional.empty(); + } + var recipeInput = new SingleRecipeInput(this.items.get(0)); + return this.quickCheck.getRecipeFor(recipeInput, se); } // Interrupt-Passive - if the currently smelting item was removed in some other way @@ -125,12 +127,12 @@ public abstract class AbstractFurnaceBlockEntityMixin extends BaseContainerBlock shift = At.Shift.BEFORE, value = "FIELD", opcode = Opcodes.PUTFIELD, - target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;cookingProgress:I" + target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;cookingTimer:I" ), slice = @Slice( from = @At( value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;burn(Lnet/minecraft/core/RegistryAccess;Lnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/core/NonNullList;I)Z" + target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;burn(Lnet/minecraft/core/NonNullList;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)V" ), to = @At( value = "INVOKE", @@ -138,18 +140,19 @@ public abstract class AbstractFurnaceBlockEntityMixin extends BaseContainerBlock ) ) ) - private static void impl$onResetCookTimePassive(final Level level, final BlockPos pos, final BlockState state, + private static void impl$onResetCookTimePassive(final ServerLevel level, final BlockPos pos, final BlockState state, @Coerce final AbstractFurnaceBlockEntityMixin entity, final CallbackInfo ci) { - entity.impl$callInteruptSmeltEvent(); + entity.impl$callInterruptSmeltEvent(); } - private void impl$callInteruptSmeltEvent() { - if (this.cookingProgress > 0) { + @Override + public void impl$callInterruptSmeltEvent() { + if (this.cookingTimer > 0) { final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(this.items.get(1)); final Cause cause = PhaseTracker.getInstance().currentCause(); final var recipe = this.bridge$getCurrentRecipe(); final CookingEvent.Interrupt event = SpongeEventFactory.createCookingEventInterrupt(cause, (FurnaceBlockEntity) this, Optional.of(fuel), - recipe.map(r -> (CookingRecipe) r.value()), recipe.map(r -> (ResourceKey) (Object) r.id())); + recipe.map(r -> (CookingRecipe) r.value()), recipe.map(r -> (ResourceKey) (Object) r.id().identifier())); SpongeCommon.post(event); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BannerBlockEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BannerBlockEntityMixin.java index 2c71c51ae6e..3d3b97ff592 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BannerBlockEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BannerBlockEntityMixin.java @@ -29,8 +29,10 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.entity.BannerBlockEntity; import org.spongepowered.api.data.type.DyeColor; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.common.bridge.CustomNameableBridge; import org.spongepowered.common.bridge.world.level.block.entity.BannerBlockEntityBridge; @@ -41,12 +43,13 @@ @Mixin(BannerBlockEntity.class) public abstract class BannerBlockEntityMixin extends BlockEntityMixin implements BannerBlockEntityBridge, CustomNameableBridge { - @Shadow private net.minecraft.world.item.DyeColor baseColor; + @Shadow @Final @org.spongepowered.asm.mixin.Mutable private net.minecraft.world.item.DyeColor baseColor; @Shadow @Nullable private Component name; + @Unique private void impl$markDirtyAndUpdate() { this.shadow$setChanged(); - if (this.level != null && !this.level.isClientSide) { + if (this.level != null && !this.level.isClientSide()) { ((ServerLevel) this.level).getChunkSource().blockChanged(this.shadow$getBlockPos()); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BeaconBlockEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BeaconBlockEntityMixin.java index 6cd5a1d20fb..719bcb7bed5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BeaconBlockEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BeaconBlockEntityMixin.java @@ -24,13 +24,15 @@ */ package org.spongepowered.common.mixin.core.world.level.block.entity; -import net.minecraft.world.effect.MobEffect; import net.minecraft.world.level.block.entity.BeaconBlockEntity; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; +import java.util.Optional; +import java.util.function.Predicate; + @Mixin(BeaconBlockEntity.class) public abstract class BeaconBlockEntityMixin extends BlockEntityMixin { @@ -43,11 +45,13 @@ public abstract class BeaconBlockEntityMixin extends BlockEntityMixin { * @param id The id * @return The potion by id, no validation */ + @SuppressWarnings({"OptionalUsedAsFieldOrParameterType", "rawtypes"}) @Redirect(method = "loadEffect", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/BeaconBlockEntity;filterEffect(Lnet/minecraft/world/effect/MobEffect;)Lnet/minecraft/world/effect/MobEffect;") + at = @At(value = "INVOKE", target = "Ljava/util/Optional;filter(Ljava/util/function/Predicate;)Ljava/util/Optional;") ) @Nullable - private static MobEffect impl$UsePotionUtilInsteadOfCheckingValidPotions(MobEffect effect) { - return effect; + private static Optional impl$UsePotionUtilInsteadOfCheckingValidPotions( + Optional instance, Predicate predicate) { + return instance; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BlockEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BlockEntityMixin.java index 819c7859b77..c037f9198ad 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BlockEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/BlockEntityMixin.java @@ -25,11 +25,15 @@ package org.spongepowered.common.mixin.core.world.level.block.entity; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.block.entity.BlockEntity; @@ -47,6 +51,7 @@ import org.spongepowered.common.data.holder.SpongeMutableDataHolder; import org.spongepowered.common.data.provider.nbt.NBTDataType; import org.spongepowered.common.data.provider.nbt.NBTDataTypes; +import org.spongepowered.common.util.Constants; import java.util.Arrays; import java.util.List; @@ -58,7 +63,7 @@ public abstract class BlockEntityMixin implements BlockEntityBridge, DataCompoun //@formatter:off @Shadow @Final private BlockEntityType type; @Shadow @Nullable private BlockState blockState; - @Shadow protected net.minecraft.world.level.Level level; + @Shadow protected Level level; @Shadow @Final protected BlockPos worldPosition; @Shadow public abstract BlockPos shadow$getBlockPos(); @@ -84,16 +89,25 @@ public abstract class BlockEntityMixin implements BlockEntityBridge, DataCompoun } @Inject(method = "saveMetadata", at = @At("RETURN")) - private void impl$writeSpongeData(final CompoundTag $$0, final CallbackInfo ci) { - if (DataUtil.syncDataToTag(this)) { - $$0.merge(this.data$getCompound()); + private void impl$writeSpongeData(final ValueOutput out, final CallbackInfo ci) { + if (out instanceof TagValueOutput tag) { + if (DataUtil.syncDataToTag(this)) { + // TODO - technically this shouldn't be used but we can access the build result + // here, ideally we may have to consider migrating the existing data serialization + // process to the new "style". + tag.buildResult().merge(this.data$getCompound()); + } } } @Inject(method = "loadWithComponents", at = @At("RETURN")) - private void impl$readSpongeData(final CompoundTag compound, HolderLookup.Provider $$1, final CallbackInfo ci) { + private void impl$readSpongeData(final ValueInput input, final CallbackInfo ci) { // TODO If we are in Forge data is already present - this.data$setCompound(compound); // For vanilla we set the incoming nbt + final var spongeData = input.read(Constants.Sponge.Data.V3.SPONGE_TAG_KEY, CustomData.CODEC); + if (spongeData.isEmpty()) { + return; + } + this.data$setCompound(spongeData.get().copyTag()); // For vanilla we set the incoming nbt // Deserialize custom data... DataUtil.syncTagToData(this); this.data$setCompound(null); // done reading diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CampfireBlockEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CampfireBlockEntityMixin.java index a659ee49a2f..157515157e2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CampfireBlockEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CampfireBlockEntityMixin.java @@ -26,12 +26,14 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.CampfireCookingRecipe; import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.RecipeManager; import net.minecraft.world.item.crafting.SingleRecipeInput; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.CampfireBlockEntity; import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.api.ResourceKey; @@ -49,9 +51,9 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.bridge.world.level.block.entity.CampfireBlockEntityBridge; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.inventory.adapter.impl.slots.SlotAdapter; import org.spongepowered.common.item.util.ItemStackUtil; @@ -61,7 +63,7 @@ import java.util.Optional; @Mixin(CampfireBlockEntity.class) -public abstract class CampfireBlockEntityMixin implements CampfireBlockEntityBridge { +public abstract class CampfireBlockEntityMixin { // @Formatter:off @Shadow @Final private NonNullList items; @@ -76,8 +78,9 @@ public abstract class CampfireBlockEntityMixin implements CampfireBlockEntityBri // Tick up @Inject(method = "cookTick", locals = LocalCapture.CAPTURE_FAILEXCEPTION, at = @At(value = "FIELD", target = "Lnet/minecraft/world/level/block/entity/CampfireBlockEntity;cookingProgress:[I", ordinal = 1)) - private static void impl$canCook(final Level level, final BlockPos pos, final BlockState state, final CampfireBlockEntity self, - final CallbackInfo ci, final boolean hasChanged, final int i, final ItemStack itemStack) { + private static void impl$canCook(final ServerLevel level, final BlockPos pos, final BlockState state, final CampfireBlockEntity self, + final RecipeManager.CachedCheck check, + final CallbackInfo ci, final boolean hasChanged, final int i, final ItemStack itemStack) { final CampfireBlockEntityMixin mixinSelf = (CampfireBlockEntityMixin) (Object) self; mixinSelf.impl$currentIndex = i; final boolean isEmpty = itemStack.isEmpty(); @@ -86,7 +89,7 @@ public abstract class CampfireBlockEntityMixin implements CampfireBlockEntityBri final ItemStackSnapshot stack = ItemStackUtil.snapshotOf(mixinSelf.items.get(i)); final RecipeHolder recipe = mixinSelf.impl$cookingRecipe[i]; final CookingEvent.Tick event = SpongeEventFactory.createCookingEventTick(cause, (Campfire) self, stack, Optional.empty(), - Optional.ofNullable(recipe).map(r -> (CookingRecipe) r.value()), Optional.ofNullable(recipe).map(r -> (ResourceKey) (Object) r.id())); + Optional.ofNullable(recipe).map(r -> (CookingRecipe) r.value()), Optional.ofNullable(recipe).map(r -> (ResourceKey) (Object) r.id().identifier())); SpongeCommon.post(event); if (event.isCancelled()) { mixinSelf.cookingProgress[i]--; @@ -97,42 +100,48 @@ public abstract class CampfireBlockEntityMixin implements CampfireBlockEntityBri @Inject(method = "cookTick", locals = LocalCapture.CAPTURE_FAILEXCEPTION, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/Containers;dropItemStack(Lnet/minecraft/world/level/Level;DDDLnet/minecraft/world/item/ItemStack;)V")) - private static void impl$assembleCampfireResult(final Level level, final BlockPos pos, final BlockState state, - final CampfireBlockEntity self, final CallbackInfo ci, final boolean hasChanged, final int i, + private static void impl$assembleCampfireResult(final ServerLevel level, final BlockPos pos, final BlockState state, + final CampfireBlockEntity self, final RecipeManager.CachedCheck check, + final CallbackInfo ci, final boolean hasChanged, final int i, final ItemStack itemStack, final SingleRecipeInput recipeInput, final ItemStack itemStack1) { final Cause cause = PhaseTracker.getInstance().currentCause(); final CampfireBlockEntityMixin mixinSelf = (CampfireBlockEntityMixin) (Object) self; final SlotTransaction transaction = new SlotTransaction(((Campfire) self).inventory().slot(i).get(), ItemStackUtil.snapshotOf(itemStack1), ItemStackSnapshot.empty()); final RecipeHolder recipe = mixinSelf.impl$cookingRecipe[i]; final CookingEvent.Finish event = SpongeEventFactory.createCookingEventFinish(cause, (Campfire) self, - Optional.empty(), Optional.ofNullable(recipe).map(r -> (CookingRecipe) r.value()), Optional.ofNullable(recipe).map(r -> (ResourceKey) (Object) r.id()), + Optional.empty(), Optional.ofNullable(recipe).map(r -> (CookingRecipe) r.value()), Optional.ofNullable(recipe).map(r -> (ResourceKey) (Object) r.id().identifier()), Collections.singletonList(transaction)); SpongeCommon.post(event); mixinSelf.impl$cookingRecipe[i] = null; mixinSelf.impl$pendingSlotTransaction = transaction; } - @Inject(method = "cookTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;sendBlockUpdated(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;I)V")) - private static void impl$setCookingEventFinishResult(final Level level, final BlockPos pos, final BlockState state, final CampfireBlockEntity entity, final CallbackInfo ci) { - final CampfireBlockEntityMixin mixinSelf = (CampfireBlockEntityMixin) (Object) entity; + @Inject(method = "cookTick", at = @At(value = "INVOKE", + target = "Lnet/minecraft/server/level/ServerLevel;sendBlockUpdated(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;I)V" + ), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + private static void impl$setCookingEventFinishResult( + final ServerLevel level, final BlockPos pos, final BlockState state, + final CampfireBlockEntity self, final RecipeManager.CachedCheck $$4, + final CallbackInfo ci, final boolean $$5, final int $$6, final ItemStack $$7, final SingleRecipeInput $$8, + final ItemStack $$9 + ) { + final CampfireBlockEntityMixin mixinSelf = (CampfireBlockEntityMixin) (Object) self; mixinSelf.impl$pendingSlotTransaction.custom().ifPresent(item -> mixinSelf.items.set(((SlotAdapter) mixinSelf.impl$pendingSlotTransaction.slot()).getOrdinal(), ItemStackUtil.fromSnapshotToNative(item))); mixinSelf.impl$pendingSlotTransaction = null; } - @Override - public void bridge$placeRecipe(final RecipeHolder recipe) { - for(int i = 0; i < this.items.size(); ++i) { - final ItemStack itemstack = this.items.get(i); - if (itemstack.isEmpty()) { - this.impl$cookingRecipe[i] = recipe; - return; - } - } + @Inject(method = "placeFood", at = @At(value = "INVOKE", target = "Ljava/util/Optional;get()Ljava/lang/Object;"), + locals = LocalCapture.CAPTURE_FAILHARD) + private void impl$captureRecipeUsage( + final ServerLevel $$0, final LivingEntity $$1, final ItemStack $$2, final CallbackInfoReturnable cir, + final int i, final ItemStack stack, final Optional> recipeOptional) { + // We know that the crafting recipe will exist because mojang checked for us + this.impl$cookingRecipe[i] = recipeOptional.get(); } @Redirect(method = "cookTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;isItemEnabled(Lnet/minecraft/world/flag/FeatureFlagSet;)Z")) - private static boolean impl$checkInfiniteCookingTime(final ItemStack instance, final FeatureFlagSet featureFlagSet, final Level level, final BlockPos blockPos, + private static boolean impl$checkInfiniteCookingTime(final ItemStack instance, final FeatureFlagSet featureFlagSet, final ServerLevel level, final BlockPos blockPos, final BlockState state, final CampfireBlockEntity self) { final int cookingTime = ((CampfireBlockEntityMixin) (Object) self).cookingTime[((CampfireBlockEntityMixin) (Object) self).impl$currentIndex]; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CommandBlockEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CommandBlockEntityMixin.java index d9b9b9b3e83..5d0547ace32 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CommandBlockEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CommandBlockEntityMixin.java @@ -25,6 +25,7 @@ package org.spongepowered.common.mixin.core.world.level.block.entity; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.BaseCommandBlock; import net.minecraft.world.level.block.entity.CommandBlockEntity; import org.spongepowered.api.event.Cause; @@ -33,14 +34,18 @@ import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.accessor.world.level.BaseCommandBlockAccessor; import org.spongepowered.common.bridge.commands.CommandSourceProviderBridge; import org.spongepowered.common.bridge.permissions.SubjectBridge; // TODO: dualspiral - should this go on the child CommandBlockLogic? @Mixin(CommandBlockEntity.class) -public abstract class CommandBlockEntityMixin implements SubjectBridge, CommandSourceProviderBridge { +public abstract class CommandBlockEntityMixin extends BlockEntityMixin implements SubjectBridge, CommandSourceProviderBridge { + // @formatter:off @Shadow @Final private BaseCommandBlock commandBlock; + // @formatter:on @Override public String bridge$getSubjectCollectionIdentifier() { @@ -54,7 +59,13 @@ public abstract class CommandBlockEntityMixin implements SubjectBridge, CommandS @Override public CommandSourceStack bridge$getCommandSource(final Cause cause) { - return this.commandBlock.createCommandSourceStack(); + if (this.level instanceof ServerLevel sl) { + return this.commandBlock.createCommandSourceStack(sl, (((BaseCommandBlockAccessor) this.commandBlock).invoker$createSource(sl))); + } + return this.commandBlock.createCommandSourceStack( + SpongeCommon.server().overworld(), + (((BaseCommandBlockAccessor) this.commandBlock).invoker$createSource(SpongeCommon.server().overworld())) + ); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CommandBlockEntity_Mixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CommandBlockEntity_Mixin.java index 3d54e86e9f4..c3ab676dcb3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CommandBlockEntity_Mixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/CommandBlockEntity_Mixin.java @@ -24,12 +24,19 @@ */ package org.spongepowered.common.mixin.core.world.level.block.entity; +import net.kyori.adventure.audience.MessageType; +import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.text.Component; +import net.minecraft.commands.CommandSourceStack; import net.minecraft.world.level.block.entity.CommandBlockEntity; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.spongepowered.api.event.Cause; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.SubjectProxy; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.mixin.core.world.BaseCommandBlockMixin; @Mixin(targets = "net.minecraft.world.level.block.entity.CommandBlockEntity$1") @@ -43,4 +50,25 @@ public abstract class CommandBlockEntity_Mixin extends BaseCommandBlockMixin imp public Subject subject() { return (Subject) this.this$0; } + + @Override + public CommandSourceStack bridge$getCommandSource(final Cause cause) { + if (!(this.this$0.getLevel() instanceof net.minecraft.server.level.ServerLevel serverLevel)) { + return null; + } + return this.createCommandSourceStack(serverLevel, this.shadow$createSource(serverLevel)); + } + + @Override + @SuppressWarnings({"deprecation", "UnstableApiUsage"}) + public void sendMessage(final @NonNull Identity identity, final @NonNull Component message, final @NonNull MessageType type) { + if (!(this.this$0.getLevel() instanceof net.minecraft.server.level.ServerLevel serverLevel)) { + return; + } + final var source = this.shadow$createSource(serverLevel); + if (source == null) { + return; + } + source.sendSystemMessage(SpongeAdventure.asVanilla(message)); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/LecternBlockEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/LecternBlockEntityMixin.java index bb91ec378a9..885ec0481d1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/LecternBlockEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/LecternBlockEntityMixin.java @@ -25,6 +25,7 @@ package org.spongepowered.common.mixin.core.world.level.block.entity; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.block.entity.LecternBlockEntity; import org.checkerframework.checker.nullness.qual.Nullable; @@ -37,14 +38,14 @@ public abstract class LecternBlockEntityMixin implements CommandSourceProviderBridge { @Shadow - private CommandSourceStack createCommandSourceStack(@Nullable final Player playerEntity) { + private CommandSourceStack createCommandSourceStack(@Nullable final Player playerEntity, final ServerLevel $$1) { throw new AssertionError("This method should be a shadow."); } @Override public CommandSourceStack bridge$getCommandSource(final Cause cause) { // We assume that if a player is in the Cause, they're the one reading the book. - return this.createCommandSourceStack(cause.first(Player.class).orElse(null)); + return this.createCommandSourceStack(cause.first(Player.class).orElse(null), cause.first(ServerLevel.class).orElse(null)); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/SignBlockEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/SignBlockEntityMixin.java index 39e5760bd6c..44cd67bf139 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/SignBlockEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/entity/SignBlockEntityMixin.java @@ -26,9 +26,9 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.SignBlockEntity; import org.spongepowered.api.event.Cause; import org.spongepowered.api.service.permission.PermissionService; @@ -41,8 +41,8 @@ @Mixin(SignBlockEntity.class) public abstract class SignBlockEntityMixin extends BlockEntityMixin implements SubjectBridge, CommandSourceProviderBridge { - // @formatter:off - @Shadow private static CommandSourceStack shadow$createCommandSourceStack(final Player $$0, final Level $$1, final BlockPos $$2) { + // @formatter:offs + @Shadow private static CommandSourceStack shadow$createCommandSourceStack(final Player $$0, final ServerLevel $$1, final BlockPos $$2) { throw new UnsupportedOperationException("Shadowed createCommandSourceStack"); } // @formatter:on @@ -59,7 +59,10 @@ public abstract class SignBlockEntityMixin extends BlockEntityMixin implements S @Override public CommandSourceStack bridge$getCommandSource(final Cause cause) { - return SignBlockEntityMixin.shadow$createCommandSourceStack(cause.first(ServerPlayer.class).orElse(null), this.level, this.worldPosition); + if (!(this.level instanceof ServerLevel serverLevel)) { + throw new UnsupportedOperationException("cannot use a command source on a non-server level"); + } + return SignBlockEntityMixin.shadow$createCommandSourceStack(cause.first(ServerPlayer.class).orElse(null), serverLevel, this.worldPosition); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/state/BlockStateMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/state/BlockStateMixin.java index bf80871b97d..0ac217ef7a7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/state/BlockStateMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/block/state/BlockStateMixin.java @@ -24,8 +24,6 @@ */ package org.spongepowered.common.mixin.core.world.level.block.state; -import com.mojang.serialization.MapCodec; -import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; @@ -42,8 +40,8 @@ @Mixin(BlockState.class) public abstract class BlockStateMixin extends BlockBehaviour.BlockStateBase implements BlockStateBridge { - protected BlockStateMixin(Block $$0, Reference2ObjectArrayMap, Comparable> $$1, MapCodec $$2) { - super($$0, $$1, $$2); + protected BlockStateMixin(Block owner, Property[] propertyKeys, Comparable[] propertyValues) { + super(owner, propertyKeys, propertyValues); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/border/WorldBorderMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/border/WorldBorderMixin.java index b8c50cba2e8..9a3f4538307 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/border/WorldBorderMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/border/WorldBorderMixin.java @@ -32,15 +32,16 @@ import org.spongepowered.api.event.world.ChangeWorldBorderEvent; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.world.level.border.WorldBorderBridge; import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.util.SpongeTicks; import org.spongepowered.common.world.border.SpongeWorldBorderBuilder; -import java.time.Duration; import java.util.Optional; import java.util.function.Supplier; @@ -59,9 +60,9 @@ public abstract class WorldBorderMixin implements WorldBorderBridge { private void impl$onSetCenter(final double x, final double z, final CallbackInfo ci) { if (this.impl$fireEvent) { final Supplier proposed = - () -> new SpongeWorldBorderBuilder().from(this) - .center(x, z) - .build(); + () -> new SpongeWorldBorderBuilder().from(this) + .center(x, z) + .build(); if (this.impl$suppressOriginalAction(proposed)) { ci.cancel(); } @@ -72,9 +73,9 @@ public abstract class WorldBorderMixin implements WorldBorderBridge { private void impl$onSetSize(final double size, final CallbackInfo ci) { if (this.impl$fireEvent) { final Supplier proposed = - () -> new SpongeWorldBorderBuilder().from(this) - .initialDiameter(size) - .build(); + () -> new SpongeWorldBorderBuilder().from(this) + .initialDiameter(size) + .build(); if (this.impl$suppressOriginalAction(proposed)) { ci.cancel(); } @@ -82,14 +83,14 @@ public abstract class WorldBorderMixin implements WorldBorderBridge { } @Inject(method = "lerpSizeBetween", at = @At(value = "HEAD"), cancellable = true) - private void impl$onLerping(final double initial, final double target, final long milliseconds, final CallbackInfo ci) { + private void impl$onLerping(final double initial, final double target, final long ticks, final long delay, final CallbackInfo ci) { if (this.impl$fireEvent) { final Supplier proposed = - () -> new SpongeWorldBorderBuilder().from(this) - .initialDiameter(initial) - .targetDiameter(target) - .timeToTargetDiameter(Duration.ofMillis(milliseconds)) - .build(); + () -> new SpongeWorldBorderBuilder().from(this) + .initialDiameter(initial) + .targetDiameter(target) + .timeToTargetDiameter(SpongeTicks.ticksOrInfinite(ticks)) + .build(); if (this.impl$suppressOriginalAction(proposed)) { ci.cancel(); } @@ -100,22 +101,22 @@ public abstract class WorldBorderMixin implements WorldBorderBridge { private void impl$onSetWarningBlocks(final int distance, final CallbackInfo ci) { if (this.impl$fireEvent) { final Supplier proposed = - () -> new SpongeWorldBorderBuilder().from(this) - .warningDistance(distance) - .build(); + () -> new SpongeWorldBorderBuilder().from(this) + .warningDistance(distance) + .build(); if (this.impl$suppressOriginalAction(proposed)) { ci.cancel(); } } } - @Inject(method = "setDamageSafeZone", at = @At(value = "HEAD"), cancellable = true) + @Inject(method = "setSafeZone", at = @At(value = "HEAD"), cancellable = true) private void impl$onSetDamageSafeZone(final double size, final CallbackInfo ci) { if (this.impl$fireEvent) { final Supplier proposed = - () -> new SpongeWorldBorderBuilder().from(this) - .safeZone(size) - .build(); + () -> new SpongeWorldBorderBuilder().from(this) + .safeZone(size) + .build(); if (this.impl$suppressOriginalAction(proposed)) { ci.cancel(); } @@ -126,9 +127,9 @@ public abstract class WorldBorderMixin implements WorldBorderBridge { private void impl$onSetDamagePerBlock(final double damagePerBlock, final CallbackInfo ci) { if (this.impl$fireEvent) { final Supplier proposed = - () -> new SpongeWorldBorderBuilder().from(this) - .damagePerBlock(damagePerBlock) - .build(); + () -> new SpongeWorldBorderBuilder().from(this) + .damagePerBlock(damagePerBlock) + .build(); if (this.impl$suppressOriginalAction(proposed)) { ci.cancel(); } @@ -139,9 +140,9 @@ public abstract class WorldBorderMixin implements WorldBorderBridge { private void impl$onSetWarningTime(final int warningTime, final CallbackInfo ci) { if (this.impl$fireEvent) { final Supplier proposed = - () -> new SpongeWorldBorderBuilder().from(this) - .warningTime(Duration.ofSeconds(warningTime)) - .build(); + () -> new SpongeWorldBorderBuilder().from(this) + .warningTime(SpongeTicks.ticksOrInfinite(warningTime)) + .build(); if (this.impl$suppressOriginalAction(proposed)) { ci.cancel(); } @@ -159,16 +160,16 @@ public abstract class WorldBorderMixin implements WorldBorderBridge { if (world.isPresent()) { final org.spongepowered.api.world.border.WorldBorder originalNewBorder = supplier.get(); final ChangeWorldBorderEvent.World event = SpongeEventFactory.createChangeWorldBorderEventWorld( - PhaseTracker.getInstance().currentCause(), - Optional.of(originalNewBorder), - Optional.of(originalNewBorder), - Optional.of(this.bridge$asImmutable()), - world.get() + PhaseTracker.getInstance().currentCause(), + Optional.of(originalNewBorder), + Optional.of(originalNewBorder), + Optional.of(this.bridge$asImmutable()), + world.get() ); final boolean isCancelled = Sponge.eventManager().post(event); if (!isCancelled) { final org.spongepowered.api.world.border.WorldBorder toSet = - event.newBorder().orElse((org.spongepowered.api.world.border.WorldBorder) WorldBorder.DEFAULT_SETTINGS); + event.newBorder().orElse((org.spongepowered.api.world.border.WorldBorder) (Object) WorldBorder.Settings.DEFAULT); if (originalNewBorder != toSet) { // set the values, suppress this call. this.impl$fireEvent = false; @@ -184,13 +185,13 @@ public abstract class WorldBorderMixin implements WorldBorderBridge { @Override public org.spongepowered.api.world.border.WorldBorder bridge$asImmutable() { - return (org.spongepowered.api.world.border.WorldBorder) ((WorldBorder) (Object) this).createSettings(); + return (org.spongepowered.api.world.border.WorldBorder) (Object) new WorldBorder.Settings(((WorldBorder) (Object) this)); } @Override public org.spongepowered.api.world.border.@Nullable WorldBorder bridge$applyFrom(final org.spongepowered.api.world.border.WorldBorder worldBorder) { if (!this.impl$fireEvent) { - ((WorldBorder) (Object) this).applySettings((WorldBorder.Settings) worldBorder); + this.impl$applySettings((WorldBorder.Settings) (Object) worldBorder); return worldBorder; } @@ -201,17 +202,31 @@ public abstract class WorldBorderMixin implements WorldBorderBridge { return this.bridge$asImmutable(); } - final org.spongepowered.api.world.border.WorldBorder defaultSettings = - (org.spongepowered.api.world.border.WorldBorder) WorldBorder.DEFAULT_SETTINGS; + final var defaultSettings = (org.spongepowered.api.world.border.WorldBorder) (Object) WorldBorder.Settings.DEFAULT; toSet = event.newBorder().orElse(defaultSettings); } else { toSet = worldBorder; } this.impl$fireEvent = false; - ((WorldBorder) (Object) this).applySettings((WorldBorder.Settings) toSet); + this.impl$applySettings((WorldBorder.Settings) (Object) worldBorder); this.impl$fireEvent = true; return toSet; } + @Unique + private void impl$applySettings(WorldBorder.Settings worldBorder) { + ((WorldBorder) (Object) this).setCenter(worldBorder.centerX(), worldBorder.centerZ()); + ((WorldBorder) (Object) this).setDamagePerBlock(worldBorder.damagePerBlock()); + ((WorldBorder) (Object) this).setSafeZone(worldBorder.safeZone()); + ((WorldBorder) (Object) this).setWarningBlocks(worldBorder.warningBlocks()); + ((WorldBorder) (Object) this).setWarningTime(worldBorder.warningTime()); + if (worldBorder.lerpTime() > 0L) { + // TODO - figure out how to get the appropriate game time + ((WorldBorder) (Object) this).lerpSizeBetween(worldBorder.size(), worldBorder.lerpTarget(), worldBorder.lerpTime(), SpongeCommon.server().overworld().getGameTime()); + } else { + ((WorldBorder) (Object) this).setSize(worldBorder.lerpTime() == -1 ? worldBorder.size() : worldBorder.lerpTarget()); + } + } + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/ChunkSerializerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/ChunkSerializerMixin.java deleted file mode 100644 index 04956c160d1..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/ChunkSerializerMixin.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.level.chunk; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.ai.village.poi.PoiManager; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ImposterProtoChunk; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.ProtoChunk; -import net.minecraft.world.level.chunk.storage.ChunkSerializer; -import net.minecraft.world.level.chunk.storage.RegionStorageInfo; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.common.bridge.data.DataCompoundHolder; -import org.spongepowered.common.data.DataUtil; - -@Mixin(ChunkSerializer.class) -public abstract class ChunkSerializerMixin { - - @Inject(method = "write", at = @At(value = "RETURN")) - private static void impl$writeSpongeChunkData(final ServerLevel level, final ChunkAccess chunk, final CallbackInfoReturnable cir) { - if (!(chunk instanceof LevelChunk)) { - return; - } - - final CompoundTag compound = cir.getReturnValue(); - if (DataUtil.syncDataToTag(chunk)) { - compound.merge(((DataCompoundHolder) chunk).data$getCompound()); - } - } - - @Inject(method = "read", at = @At("RETURN")) - private static void impl$readSpongeChunkData(final ServerLevel level, final PoiManager poi, final RegionStorageInfo $$2, final ChunkPos pos, final CompoundTag compound, final CallbackInfoReturnable cir) { - if (!(cir.getReturnValue() instanceof ImposterProtoChunk imposter)) { - return; - } - - ((DataCompoundHolder) imposter.getWrapped()).data$setCompound(compound); - DataUtil.syncTagToData(imposter.getWrapped()); - ((DataCompoundHolder) imposter.getWrapped()).data$setCompound(null); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/ChunkStatusTasksMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/ChunkStatusTasksMixin.java index 0575009f31d..f09cb880962 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/ChunkStatusTasksMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/ChunkStatusTasksMixin.java @@ -96,12 +96,12 @@ static CompletableFuture generateBiomes(final WorldGenContext conte } //Sponge end } - @Inject(method = "lambda$full$2", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;setLoaded(Z)V")) + @Inject(method = "lambda$full$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;setLoaded(Z)V")) private static void impl$onLoad(final CallbackInfoReturnable cir, @Local() final LevelChunk levelChunk) { final Vector3i chunkPos = VecHelper.toVector3i(levelChunk.getPos()); if (ShouldFire.CHUNK_EVENT_BLOCKS_LOAD) { final ChunkEvent.Blocks.Load loadEvent = SpongeEventFactory.createChunkEventBlocksLoad(PhaseTracker.getInstance().currentCause(), - ((BlockChunk) levelChunk), chunkPos, (ResourceKey) (Object) levelChunk.getLevel().dimension().location()); + ((BlockChunk) levelChunk), chunkPos, (ResourceKey) (Object) levelChunk.getLevel().dimension().identifier()); SpongeCommon.post(loadEvent); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/LevelChunkMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/LevelChunkMixin.java index 42e6925e80f..891c05fa968 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/LevelChunkMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/LevelChunkMixin.java @@ -25,19 +25,18 @@ package org.spongepowered.common.mixin.core.world.level.chunk; import net.minecraft.core.BlockPos; -import net.minecraft.core.Registry; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.PalettedContainerFactory; import net.minecraft.world.level.chunk.UpgradeData; import net.minecraft.world.level.levelgen.blending.BlendingData; import net.minecraft.world.ticks.LevelChunkTicks; @@ -86,13 +85,15 @@ public abstract class LevelChunkMixin extends ChunkAccess implements LevelChunkBridge, CacheKeyBridge, SpongeMutableDataHolder, SpongeDataHolderBridge, DataCompoundHolder, BlockChunk { // @formatter:off - @Shadow @Final private Level level; + @Shadow @Final Level level; @Shadow private boolean loaded; @Shadow @Nullable public abstract BlockEntity shadow$getBlockEntity(BlockPos pos, net.minecraft.world.level.chunk.LevelChunk.EntityCreationType p_177424_2_); @Shadow public abstract BlockState shadow$getBlockState(BlockPos pos); @Shadow public abstract void shadow$addEntity(net.minecraft.world.entity.Entity param0); - // @formatter:on + @Shadow public abstract void shadow$markUnsaved(); +// @formatter:on + private long impl$scheduledForUnload = -1; // delay chunk unloads private boolean impl$persistedChunk = false; @@ -103,15 +104,7 @@ public abstract class LevelChunkMixin extends ChunkAccess implements LevelChunkB private Map impl$trackedShortBlockPositions = new HashMap<>(); private @Nullable CompoundTag impl$compound; - public LevelChunkMixin( - final ChunkPos $$0, - final UpgradeData $$1, - final LevelHeightAccessor $$2, - final Registry $$3, - final long $$4, - final LevelChunkSection[] $$5, - final BlendingData $$6 - ) { + public LevelChunkMixin(ChunkPos $$0, UpgradeData $$1, LevelHeightAccessor $$2, PalettedContainerFactory $$3, long $$4, @org.jetbrains.annotations.Nullable LevelChunkSection[] $$5, @org.jetbrains.annotations.Nullable BlendingData $$6, Level level) { super($$0, $$1, $$2, $$3, $$4, $$5, $$6); } @@ -120,7 +113,7 @@ public LevelChunkMixin( private void impl$onConstruct(final Level $$0, final ChunkPos $$1, final UpgradeData $$2, final LevelChunkTicks $$3, final LevelChunkTicks $$4, final long $$5, final LevelChunkSection[] $$6, final LevelChunk.PostLoadProcessor $$7, final BlendingData $$8, final CallbackInfo ci) { - this.impl$cacheKey = ChunkPos.asLong($$1.x, $$1.z); + this.impl$cacheKey = $$1.pack(); } @Override @@ -130,12 +123,12 @@ public LevelChunkMixin( @Override public void bridge$markChunkDirty() { - this.unsaved = true; + this.shadow$markUnsaved(); } @Override public boolean bridge$isQueuedForUnload() { - return ((ChunkMapAccessor) ((ServerChunkCache) this.level.getChunkSource()).chunkMap).accessor$pendingUnloads().containsKey(this.chunkPos.toLong()); + return ((ChunkMapAccessor) ((ServerChunkCache) this.level.getChunkSource()).chunkMap).accessor$pendingUnloads().containsKey(this.chunkPos.pack()); } @Override @@ -365,7 +358,7 @@ public LevelChunkMixin( public String toString() { return new StringJoiner(", ", LevelChunkMixin.class.getSimpleName() + "[", "]") .add("World=" + this.level) - .add("Position=" + this.chunkPos.x + ":" + this.chunkPos.z) + .add("Position=" + this.chunkPos.x() + ":" + this.chunkPos.z()) .add("super=" + super.toString()) .toString(); } @@ -379,7 +372,7 @@ public String toString() { public boolean bridge$spawnEntity(final org.spongepowered.api.entity.Entity entity) { final net.minecraft.world.entity.Entity mcEntity = (net.minecraft.world.entity.Entity) entity; final BlockPos blockPos = mcEntity.blockPosition(); - if (this.chunkPos.x == blockPos.getX() >> 4 && this.chunkPos.z == blockPos.getZ() >> 4) { + if (this.chunkPos.x() == blockPos.getX() >> 4 && this.chunkPos.z() == blockPos.getZ() >> 4) { // Calling addEntity on the chunk only adds them to storage, // we need to redirect this to add to the world. // @@ -404,14 +397,14 @@ public String toString() { @Override public DataTransactionResult bridge$offer(final Key<@NonNull ? extends Value> key, final E value) { final DataTransactionResult result = DataHolderProcessor.bridge$offer(this, key, value); - this.unsaved = true; + this.shadow$markUnsaved(); return result; } @Override public DataTransactionResult bridge$remove(final Key<@NonNull ? extends Value> key) { final DataTransactionResult result = DataHolderProcessor.bridge$remove(this, key); - this.unsaved = true; + this.shadow$markUnsaved(); return result; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/SerializableChunkDataMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/SerializableChunkDataMixin.java new file mode 100644 index 00000000000..d4b215e36af --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/SerializableChunkDataMixin.java @@ -0,0 +1,139 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.level.chunk; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.PalettedContainerFactory; +import net.minecraft.world.level.chunk.storage.SerializableChunkData; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.world.SerializationBehavior; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.bridge.data.DataCompoundHolder; +import org.spongepowered.common.bridge.data.SpongeDataHolderBridge; +import org.spongepowered.common.bridge.world.level.chunk.storage.SerializableChunkDataBridge; +import org.spongepowered.common.bridge.world.level.storage.ServerLevelDataBridge; +import org.spongepowered.common.data.DataUtil; + +@Mixin(SerializableChunkData.class) +public abstract class SerializableChunkDataMixin implements SerializableChunkDataBridge { + + private DataContainer impl$dataContainer; + private CompoundTag impl$tag; + private SerializationBehavior impl$serializationBehavior; + + + @Inject(method = "copyOf", at = @At(value = "RETURN")) + private static void impl$copyOfSpongeData(final ServerLevel level, final ChunkAccess chunkAccess, final CallbackInfoReturnable cir) { + final var bridge = (SerializableChunkDataBridge) (Object) cir.getReturnValue(); + if (level.getLevelData() instanceof final ServerLevelDataBridge levelBridge) { + final var behavior = levelBridge.bridge$serializationBehavior().orElse(SerializationBehavior.AUTOMATIC); + bridge.bridge$setSerializationBehavior(behavior); + } + + if (chunkAccess instanceof final LevelChunk levelChunk) { + bridge.bridge$setTrackerData(levelChunk); + bridge.bridge$setDataHolderData(levelChunk); + } + } + + @Override + public void bridge$setSerializationBehavior(final SerializationBehavior serializationBehavior) { + this.impl$serializationBehavior = serializationBehavior; + } + + @Override + public void bridge$setDataHolderData(final LevelChunk levelChunk) { + this.impl$tag = null; + if (DataUtil.syncDataToTag(levelChunk)) { + this.impl$tag = ((DataCompoundHolder) levelChunk).data$getCompound(); + } + } + + @Inject(method = "write", at = @At(value = "HEAD"), cancellable = true) + private void tracker$beforeWrite(final CallbackInfoReturnable cir) { + switch (this.impl$serializationBehavior) { + case AUTOMATIC, MANUAL -> {} // write normally + default -> cir.setReturnValue(null); + } + + } + + @Inject(method = "write", at = @At(value = "RETURN")) + private void tracker$writeSpongeData(final CallbackInfoReturnable cir) { + final CompoundTag level = cir.getReturnValue(); + this.bridge$writeTrackerData(level); + this.bridge$writeDataHolderData(level); + } + + @Override + public void bridge$writeDataHolderData(final CompoundTag level) { + if (this.impl$tag != null) { + level.merge(this.impl$tag); + } + } + + @Inject(method = "parse", at = @At(value = "TAIL")) + private static void tracker$parseSpongeData( + final LevelHeightAccessor ha, final PalettedContainerFactory pcf, final CompoundTag tag, + final CallbackInfoReturnable cir + ) { + final var bridge = (SerializableChunkDataBridge) (Object) cir.getReturnValue(); + bridge.bridge$parseTrackerData(tag); + bridge.bridge$parseDataHolderData(tag); + } + + @Override + public void bridge$parseDataHolderData(final CompoundTag tag) { + this.impl$dataContainer = DataUtil.convertTagToContainer(tag); + } + + @Redirect(method = "read", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/ChunkAccess;setLightCorrect(Z)V")) + private void impl$readSpongeLevelData(final ChunkAccess chunkAccess, final boolean lightCorrect) { + chunkAccess.setLightCorrect(lightCorrect); + + if (!(chunkAccess instanceof LevelChunk levelChunk)) { + return; + } + + this.bridge$readTrackerDataFrom(levelChunk); + this.bridge$readDataHolderDataFrom(levelChunk); + } + + @Override + public void bridge$readDataHolderDataFrom(final LevelChunk levelChunk) { + DataUtil.deserializeSpongeData((SpongeDataHolderBridge) levelChunk, this.impl$dataContainer); + } + + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/storage/EntityStorageMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/storage/EntityStorageMixin.java index 5eecfb80900..bf43ea2cd64 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/storage/EntityStorageMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/storage/EntityStorageMixin.java @@ -77,7 +77,7 @@ public abstract class EntityStorageMixin { final Vector3i chunkPos = VecHelper.toVector3i($$0x); final SpongeEntityChunk entities = new SpongeEntityChunk(this.level, chunkPos, cir.getReturnValue().getEntities().collect(Collectors.toList())); final ChunkEvent.Entities.Load loadEvent = SpongeEventFactory.createChunkEventEntitiesLoad(PhaseTracker.getInstance().currentCause(), - entities, chunkPos, (ResourceKey) (Object) this.level.dimension().location()); + entities, chunkPos, (ResourceKey) (Object) this.level.dimension().identifier()); SpongeCommon.post(loadEvent); @@ -94,7 +94,7 @@ public abstract class EntityStorageMixin { final SpongeEntityChunk entities = new SpongeEntityChunk(this.level, chunkPos, $$0.getEntities().collect(Collectors.toList())); final ChunkEvent.Entities.Save.Pre saveEvent = SpongeEventFactory.createChunkEventEntitiesSavePre(PhaseTracker.getInstance().currentCause(), - entities, chunkPos, (ResourceKey) (Object) this.level.dimension().location()); + entities, chunkPos, (ResourceKey) (Object) this.level.dimension().identifier()); if (SpongeCommon.post(saveEvent)) { return null; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/storage/IOWorkerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/storage/IOWorkerMixin.java index 5f63565d00f..80794617996 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/storage/IOWorkerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/chunk/storage/IOWorkerMixin.java @@ -34,7 +34,7 @@ import net.minecraft.nbt.visitors.CollectFields; import net.minecraft.nbt.visitors.FieldSelector; import net.minecraft.resources.ResourceKey; -import net.minecraft.util.thread.ProcessorMailbox; +import net.minecraft.util.thread.PriorityConsecutiveExecutor; import net.minecraft.util.thread.StrictQueue; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; @@ -48,6 +48,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Coerce; import org.spongepowered.asm.mixin.injection.Inject; @@ -62,7 +63,7 @@ import org.spongepowered.math.vector.Vector3i; import java.util.BitSet; -import java.util.Map; +import java.util.SequencedMap; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -74,9 +75,9 @@ public abstract class IOWorkerMixin implements IOWorkerBridge { @Shadow @Final private static Logger LOGGER; @Shadow @Final private AtomicBoolean shutdownRequested; - @Shadow @Final private ProcessorMailbox mailbox; + @Shadow @Final private PriorityConsecutiveExecutor consecutiveExecutor; @Shadow @Final private RegionFileStorage storage; - @Shadow @Final private Map pendingWrites; + @Shadow @Final private SequencedMap pendingWrites; @Shadow protected abstract boolean shadow$isOldChunk(CompoundTag $$0); @Shadow protected abstract void shadow$tellStorePending(); @@ -98,17 +99,17 @@ public abstract class IOWorkerMixin implements IOWorkerBridge { private void impl$onSaved(final ChunkPos param0, final @Coerce Object param1, final CallbackInfo ci) { if (this.impl$type == SpongeIOWorkerType.CHUNK) { if (ShouldFire.CHUNK_EVENT_BLOCKS_SAVE_POST) { - final Vector3i chunkPos = new Vector3i(param0.x, 0, param0.z); + final Vector3i chunkPos = new Vector3i(param0.x(), 0, param0.z()); final ChunkEvent.Blocks.Save.Post postSave = SpongeEventFactory.createChunkEventBlocksSavePost(PhaseTracker.getInstance().currentCause(), chunkPos, - (org.spongepowered.api.ResourceKey) (Object) this.impl$dimension.location()); + (org.spongepowered.api.ResourceKey) (Object) this.impl$dimension.identifier()); SpongeCommon.post(postSave); } } else if (this.impl$type == SpongeIOWorkerType.ENTITY) { if (ShouldFire.CHUNK_EVENT_ENTITIES_SAVE_POST) { - final Vector3i chunkPos = new Vector3i(param0.x, 0, param0.z); + final Vector3i chunkPos = new Vector3i(param0.x(), 0, param0.z()); final ChunkEvent.Entities.Save.Post postSave = SpongeEventFactory.createChunkEventEntitiesSavePost(PhaseTracker.getInstance().currentCause(), chunkPos, - (org.spongepowered.api.ResourceKey) (Object) this.impl$dimension.location()); + (org.spongepowered.api.ResourceKey) (Object) this.impl$dimension.identifier()); SpongeCommon.post(postSave); } } @@ -168,16 +169,20 @@ private CompletableFuture createOldDataForRegion(final int x, final int }); } + @Unique private CompletableFuture impl$submitTaskCancellable(final Supplier> supplier) { - final CompletableFuture future = this.mailbox.askEither(processor -> new StrictQueue.IntRunnable(0, () -> { + final CompletableFuture future = this.consecutiveExecutor.scheduleWithResult(0, f -> { if (!this.shutdownRequested.get()) { - processor.tell(supplier.get()); + supplier.get() + .ifLeft(f::complete) + .ifRight(f::completeExceptionally); + f.complete(supplier.get().orThrow()); } else { - processor.tell(Either.right(SpongeUnloadedChunkException.INSTANCE)); //Sponge: Complete exceptionally if shutdown was requested + f.completeExceptionally(SpongeUnloadedChunkException.INSTANCE); //Sponge: Complete exceptionally if shutdown was requested } this.shadow$tellStorePending(); - })); + }); //Sponge start: Complete exceptionally if shutdown was requested if (this.shutdownRequested.get()) { @@ -202,7 +207,7 @@ private CompletableFuture createOldDataForRegion(final int x, final int @Override public CompletableFuture bridge$onIdle() { // Schedules a task in the SHUTDOWN priority which will be executed after everything else. - return this.mailbox.ask(h -> new StrictQueue.IntRunnable(2, () -> h.tell(null))); + return this.consecutiveExecutor.scheduleWithResult(2, (future) -> future.complete(null)); } @WrapMethod(method = "tellStorePending") @@ -223,14 +228,13 @@ private CompletableFuture createOldDataForRegion(final int x, final int return original.call(instance, supplier); } - @Inject(method = "close", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/thread/ProcessorMailbox;ask(Ljava/util/function/Function;)Ljava/util/concurrent/CompletableFuture;")) + @Inject(method = "close", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;waitForShutdown()V")) private void impl$onMailboxClose(final CallbackInfo ci) { if (this.impl$haltStore) { // Complete the never processed stores to make sure we don't left promises hanging. - this.mailbox.ask(h -> new StrictQueue.IntRunnable(2, () -> { + this.consecutiveExecutor.schedule(new StrictQueue.RunnableWithPriority(2, () -> { this.pendingWrites.values().forEach(p -> p.accessor$result().complete(null)); this.pendingWrites.clear(); - h.tell(null); })); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/dimension/DimensionTypeMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/dimension/DimensionTypeMixin.java index 0c8ac09926c..5b479180938 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/dimension/DimensionTypeMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/dimension/DimensionTypeMixin.java @@ -58,7 +58,7 @@ public abstract class DimensionTypeMixin implements DimensionTypeBridge { return this.impl$createDragonFight; } - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/ExtraCodecs;catchDecoderException(Lcom/mojang/serialization/Codec;)Lcom/mojang/serialization/Codec;")) + @Redirect(method = "createDirectCodec", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/ExtraCodecs;catchDecoderException(Lcom/mojang/serialization/Codec;)Lcom/mojang/serialization/Codec;")) private static Codec impl$onWrapCodec(final Codec codec) { return ExtraCodecs.catchDecoderException(SpongeDimensionTypes.injectCodec(codec)); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/dimension/LevelStemMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/dimension/LevelStemMixin.java index d2b159b0593..12845f1430a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/dimension/LevelStemMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/dimension/LevelStemMixin.java @@ -30,8 +30,6 @@ import net.minecraft.world.level.GameType; import net.minecraft.world.level.dimension.LevelStem; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.data.DataManipulator; -import org.spongepowered.api.data.Keys; import org.spongepowered.api.world.SerializationBehavior; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -145,37 +143,9 @@ public abstract class LevelStemMixin implements LevelStemBridge, SpongeDataHolde } @Override - public LevelStem bridge$decorateData(final DataManipulator data) { - this.impl$displayName = data.getOrNull(Keys.DISPLAY_NAME); - this.impl$gameMode = (GameType) (Object) data.get(Keys.GAME_MODE).orElse(null); - this.impl$difficulty = (Difficulty) (Object) data.get(Keys.WORLD_DIFFICULTY).orElse(null); - this.impl$serializationBehavior = data.getOrElse(Keys.SERIALIZATION_BEHAVIOR, SerializationBehavior.AUTOMATIC); - this.impl$viewDistance = data.getOrNull(Keys.VIEW_DISTANCE); - this.impl$spawnPosition = data.getOrNull(Keys.SPAWN_POSITION); - this.impl$loadOnStartup = data.getOrElse(Keys.IS_LOAD_ON_STARTUP, true); - this.impl$performsSpawnLogic = data.getOrElse(Keys.PERFORM_SPAWN_LOGIC, false); - this.impl$hardcore = data.getOrNull(Keys.HARDCORE); - this.impl$allowCommands = data.getOrNull(Keys.COMMANDS); - this.impl$pvp = data.getOrNull(Keys.PVP); - this.impl$seed = data.getOrNull(Keys.SEED); - return (LevelStem) (Object) this; - } - - @Override - public SpongeWorldTemplate.SpongeDataSection bridge$createData() { - return new SpongeWorldTemplate.SpongeDataSection( - this.impl$displayName, - this.impl$gameMode, - this.impl$difficulty, - this.impl$serializationBehavior, - this.impl$viewDistance, - this.impl$spawnPosition, - this.impl$loadOnStartup, - this.impl$performsSpawnLogic, - this.impl$hardcore, - this.impl$allowCommands, - this.impl$pvp, - this.impl$seed - ); + public boolean bridge$hasLegacyData() { + return this.impl$displayName != null || this.impl$gameMode != null || this.impl$difficulty != null || this.impl$serializationBehavior != null + || this.impl$viewDistance != null || this.impl$spawnPosition != null || this.impl$hardcore != null || this.impl$allowCommands != null + || this.impl$pvp != null || this.impl$seed != null; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/entity/PersistentEntitySectionManagerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/entity/PersistentEntitySectionManagerMixin.java index 2759957ff5d..6ac4883ddfc 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/entity/PersistentEntitySectionManagerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/entity/PersistentEntitySectionManagerMixin.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.mixin.core.world.level.entity; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.entity.EntityAccess; import net.minecraft.world.level.entity.PersistentEntitySectionManager; @@ -55,6 +56,15 @@ public class PersistentEntitySectionManagerMixin { final EntityAccess entity, final boolean callCreateCallback, final CallbackInfoReturnable cir ) { + // Section manager additions outside the main thread are an API misuse (vanilla worldgen + // routes entities through WorldGenRegion#addFreshEntity, which is chunk-scoped). If we + // somehow get here off-thread anyway (e.g. a buggy mod calling ServerLevel#addFreshEntity + // from a worldgen worker), skip the construct event rather than tripping the cause-stack + // main-thread guard. Other Sponge entity mixins (ItemEntityMixin, EntityMixin_Tracker, + // ServerLevelMixin_Tracker, ...) follow the same pattern. + if (!PhaseTracker.getWorldInstance().onSidedThread()) { + return; + } if (!(entity instanceof final EntityBridge bridge)) { return; } @@ -73,7 +83,7 @@ public class PersistentEntitySectionManagerMixin { final ServerLocation location = ServerLocation.of(sw, position.x(), position.y(), position.z()); final Vec2 rotationVector = mcEntity.getRotationVector(); final Vector3d rotation = new Vector3d(rotationVector.x, rotationVector.y, 0); - try (final CauseStackManager.StackFrame frame = PhaseTracker.SERVER.pushCauseFrame()) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getWorldInstance((ServerLevel) sw).pushCauseFrame()) { frame.pushCause(entity); final Event construct = SpongeEventFactory.createConstructEntityEventPost(frame.currentCause(), (org.spongepowered.api.entity.Entity) entity, location, rotation, ((EntityType) mcEntity.getType())); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/levelgen/WorldDimensionsMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/levelgen/WorldDimensionsMixin.java index 82bf8dc0b92..c7cc36cdbe6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/levelgen/WorldDimensionsMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/levelgen/WorldDimensionsMixin.java @@ -31,7 +31,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.hooks.PlatformHooks; import java.util.Map; @@ -52,7 +52,8 @@ private Object findOverworldStem(final Map instance, final Object key) { // ends up being a double lookup for all worlds but the overworld but I feel it is a small price to pay vs. a very complicated series // of mixins to remove retrieving the level stem settings solely for all worlds but the overworld if (val == null) { - return SpongeCommon.vanillaRegistry(Registries.LEVEL_STEM).get((ResourceKey) key); + return PlatformHooks.INSTANCE.getWorldHooks().earlyRegistryAccess(Registries.LEVEL_STEM).get((ResourceKey) key) + .map(ref -> (Object) ref.value()).orElse(null); } return val; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/saveddata/MapIdTrackerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/saveddata/MapIdTrackerMixin.java index bde3db475a7..e3467b1bd73 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/saveddata/MapIdTrackerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/saveddata/MapIdTrackerMixin.java @@ -24,33 +24,30 @@ */ package org.spongepowered.common.mixin.core.world.level.saveddata; -import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.minecraft.world.level.saveddata.maps.MapIndex; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.bridge.map.MapIdTrackerBridge; -import org.spongepowered.common.util.Constants; import java.util.OptionalInt; @Mixin(MapIndex.class) public abstract class MapIdTrackerMixin implements MapIdTrackerBridge { - @Shadow @Final private Object2IntMap usedAuxIds; + @Shadow + private int lastMapId; @Override public void bridge$setHighestMapId(final int id) { - this.usedAuxIds.put(Constants.Map.ID_COUNTS_KEY, id); + this.lastMapId = id; } @Override public OptionalInt bridge$getHighestMapId() { - final int id = this.usedAuxIds.getInt(Constants.Map.ID_COUNTS_KEY); - if (id == this.usedAuxIds.defaultReturnValue()) { - return OptionalInt.empty(); // Default return value is -1 + if (this.lastMapId == -1) { + return OptionalInt.empty(); } - return OptionalInt.of(id); + return OptionalInt.of(this.lastMapId); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/DimensionDataStorageMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/DimensionDataStorageMixin.java deleted file mode 100644 index 2e29f50e245..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/DimensionDataStorageMixin.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.core.world.level.storage; - -import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.datafix.DataFixTypes; -import net.minecraft.world.level.saveddata.SavedData; -import net.minecraft.world.level.storage.DimensionDataStorage; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import org.spongepowered.common.bridge.data.DataCompoundHolder; -import org.spongepowered.common.bridge.world.level.storage.DimensionDataStorageBridge; -import org.spongepowered.common.data.DataUtil; - -import java.io.File; -import java.util.function.BiFunction; - -@Mixin(DimensionDataStorage.class) -public abstract class DimensionDataStorageMixin implements DimensionDataStorageBridge { - - // @formatter:off - private @Nullable ResourceLocation impl$dimensionKey; - // @formatter:on - - @Inject(method = "readSavedData", at = @At(value = "RETURN", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) - public void readSpongeMapData(BiFunction var1, DataFixTypes $$1, String var2, - CallbackInfoReturnable cir, - File var3, CompoundTag rootTag) { - final SavedData savedData = cir.getReturnValue(); - if (savedData instanceof DataCompoundHolder) { - ((DataCompoundHolder) savedData).data$setCompound(rootTag); - DataUtil.syncTagToData(savedData); - ((DataCompoundHolder) savedData).data$setCompound(null); - } - } - - @Override - public void bridge$dimensionKey(final @Nullable ResourceLocation dimensionKey) { - this.impl$dimensionKey = dimensionKey; - } - - @Override - public @Nullable ResourceLocation bridge$dimensionKey() { - return this.impl$dimensionKey; - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/LevelStorageSource_LevelStorageAccessMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/LevelStorageSource_LevelStorageAccessMixin.java index 9ddd705a164..807136191f0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/LevelStorageSource_LevelStorageAccessMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/LevelStorageSource_LevelStorageAccessMixin.java @@ -24,68 +24,23 @@ */ package org.spongepowered.common.mixin.core.world.level.storage; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.core.RegistryAccess; -import net.minecraft.nbt.CompoundTag; import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.WorldData; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.common.bridge.data.DataCompoundHolder; -import org.spongepowered.common.bridge.world.level.storage.LevelStorageAccessBridge; -import org.spongepowered.common.bridge.world.level.storage.PrimaryLevelDataBridge; -import org.spongepowered.common.data.DataUtil; -import org.spongepowered.common.util.Constants; - -import java.nio.file.Path; @Mixin(LevelStorageSource.LevelStorageAccess.class) -public abstract class LevelStorageSource_LevelStorageAccessMixin implements LevelStorageAccessBridge { +public abstract class LevelStorageSource_LevelStorageAccessMixin { // @formatter:off @Shadow @Final LevelStorageSource.LevelDirectory levelDirectory; // @formatter:on - private boolean impl$dedicated = false; - - @Inject(method = "getDimensionPath", at = @At("HEAD"), cancellable = true) - public void impl$dedicatedDimensionPath(final CallbackInfoReturnable cir) { - if (this.impl$dedicated) { - cir.setReturnValue(this.levelDirectory.path()); - } - } - @ModifyArg(method = "checkLock", at = @At(value = "INVOKE", target = "Ljava/lang/IllegalStateException;(Ljava/lang/String;)V", ordinal = 0, remap = false)) private String modifyMinecraftExceptionOutputIfNotInitializationTime(final String message) { return "Lock for world " + this.levelDirectory.path() + " is no longer valid!"; } - - @WrapOperation( - method = "saveDataTag(Lnet/minecraft/core/RegistryAccess;Lnet/minecraft/world/level/storage/WorldData;Lnet/minecraft/nbt/CompoundTag;)V", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;saveLevelData(Lnet/minecraft/nbt/CompoundTag;)V" - ) - ) - private void impl$saveSpongeLevelData(final LevelStorageSource.LevelStorageAccess instance, final CompoundTag root, final Operation original, - final RegistryAccess registry, final WorldData levelData, final @Nullable CompoundTag tag) { - root.put(Constants.Sponge.Data.V2.SPONGE_DATA, ((PrimaryLevelDataBridge) levelData).bridge$writeSpongeLevelData()); - if (DataUtil.syncDataToTag(levelData)) { - root.merge(((DataCompoundHolder) levelData).data$getCompound()); - } - original.call(instance, root); - } - - @Override - public void bridge$setDedicated(final boolean dedicated) { - this.impl$dedicated = dedicated; - } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PlayerDataStorageMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PlayerDataStorageMixin.java index 449b00f5283..5853fe9133d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PlayerDataStorageMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PlayerDataStorageMixin.java @@ -24,27 +24,19 @@ */ package org.spongepowered.common.mixin.core.world.level.storage; -import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.storage.PlayerDataStorage; -import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.SpongeServer; import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; -import java.time.Instant; @Mixin(PlayerDataStorage.class) public abstract class PlayerDataStorageMixin { @@ -53,20 +45,10 @@ public abstract class PlayerDataStorageMixin { @Shadow @Final private File playerDir; // @formatter:on - @Redirect(method = "lambda$load$1", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;load(Lnet/minecraft/nbt/CompoundTag;)V")) - private void impl$readSpongePlayerData(final Player playerEntity, final CompoundTag compound) throws IOException { - playerEntity.load(compound); - if (((ServerPlayer) playerEntity).get(Keys.FIRST_DATE_JOINED).isEmpty()) { - final Path file = new File(this.playerDir, playerEntity.getStringUUID() + ".dat").toPath(); - final Instant creationTime = Files.exists(file) ? Files.readAttributes(file, BasicFileAttributes.class).creationTime().toInstant() : null; - ((SpongeServer) SpongeCommon.server()).getPlayerDataManager().readLegacyPlayerData((ServerPlayer) playerEntity, compound, creationTime); - } - ((ServerPlayer) playerEntity).offer(Keys.LAST_DATE_JOINED, Instant.now()); - } @Inject(method = "save", at = @At(value = "INVOKE", - target = "Lnet/minecraft/Util;safeReplaceFile(Ljava/nio/file/Path;Ljava/nio/file/Path;Ljava/nio/file/Path;)V", + target = "Lnet/minecraft/util/Util;safeReplaceFile(Ljava/nio/file/Path;Ljava/nio/file/Path;Ljava/nio/file/Path;)V", shift = At.Shift.AFTER)) private void impl$saveSpongePlayerData(final Player player, final CallbackInfo callbackInfo) { ((SpongeServer) SpongeCommon.server()).getPlayerDataManager().deleteLegacyPlayerData((ServerPlayer) player); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PrimaryLevelDataMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PrimaryLevelDataMixin.java index b01c9d89510..be6885b2847 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PrimaryLevelDataMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PrimaryLevelDataMixin.java @@ -25,25 +25,20 @@ package org.spongepowered.common.mixin.core.world.level.storage; import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.Codec; -import com.mojang.serialization.Dynamic; import net.kyori.adventure.text.Component; -import net.minecraft.core.BlockPos; -import net.minecraft.core.UUIDUtil; +import net.minecraft.core.GlobalPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.IntArrayTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.Difficulty; import net.minecraft.world.level.GameType; import net.minecraft.world.level.LevelSettings; +import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.gamerules.GameRules; +import net.minecraft.world.level.storage.LevelData; import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.storage.ServerLevelData; import net.minecraft.world.level.storage.WorldData; @@ -51,23 +46,15 @@ import org.spongepowered.api.world.SerializationBehavior; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.common.accessor.server.MinecraftServerAccessor; -import org.spongepowered.common.accessor.world.level.LevelSettingsAccessor; +import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.data.DataCompoundHolder; import org.spongepowered.common.bridge.world.level.dimension.LevelStemBridge; import org.spongepowered.common.bridge.world.level.storage.PrimaryLevelDataBridge; -import org.spongepowered.common.data.DataUtil; -import org.spongepowered.common.data.fixer.LegacyUUIDCodec; -import org.spongepowered.common.util.Constants; -import org.spongepowered.common.util.MapUtil; import org.spongepowered.common.util.VecHelper; +import org.spongepowered.common.world.server.SpongeMapUUIDData; import org.spongepowered.common.world.server.SpongeServerLevelData; import org.spongepowered.math.vector.Vector3i; -import java.util.Collections; import java.util.Optional; import java.util.StringJoiner; import java.util.UUID; @@ -77,26 +64,18 @@ public abstract class PrimaryLevelDataMixin implements ServerLevelData, WorldDat // @formatter:off @Shadow private LevelSettings settings; - @Shadow private BlockPos spawnPos; - @Shadow private float spawnAngle; + @Shadow private LevelData.RespawnData respawnData; @Shadow public abstract boolean shadow$isDifficultyLocked(); - @Shadow public abstract void shadow$setSpawn(BlockPos p_176143_1_, float p_176143_2_); + @Shadow public abstract void shadow$setSpawn(LevelData.RespawnData $$0); // @formatter:on - private final SpongeServerLevelData impl$spongeData = new SpongeServerLevelData(); + private SpongeServerLevelData impl$spongeData = new SpongeServerLevelData(); private DimensionType impl$dimensionType; - @Nullable private SerializationBehavior impl$serializationBehavior; - @Nullable private Component impl$displayName; - @Nullable private Integer impl$viewDistance; - private Boolean impl$pvp; + private ChunkGenerator impl$chunkGenerator; - private final BiMap impl$playerUniqueIdMap = HashBiMap.create(); - - private boolean impl$customDifficulty = false, impl$customGameType = false, impl$customSpawnPosition = false, impl$loadOnStartup, impl$performsSpawnLogic; - - private BiMap impl$mapUUIDIndex = HashBiMap.create(); + private boolean impl$customDifficulty = false, impl$customGameType = false, impl$customSpawnPosition = false; private @Nullable CompoundTag impl$compound; @Override @@ -109,6 +88,11 @@ public abstract class PrimaryLevelDataMixin implements ServerLevelData, WorldDat return this.impl$dimensionType; } + @Override + public @Nullable ChunkGenerator bridge$chunkGenerator() { + return this.impl$chunkGenerator; + } + @Override public void bridge$dimensionType(final DimensionType type, final boolean updatePlayers) { this.impl$dimensionType = type; @@ -141,70 +125,75 @@ public abstract class PrimaryLevelDataMixin implements ServerLevelData, WorldDat @Override public Optional bridge$pvp() { - return Optional.ofNullable(this.impl$pvp); + return Optional.ofNullable(this.impl$spongeData.configAdapter().get().world.pvpEnabled); } @Override public void bridge$setPvp(@Nullable final Boolean pvp) { - this.impl$pvp = pvp; + this.impl$spongeData.configAdapter().get().world.pvpEnabled = pvp; } @Override public boolean bridge$performsSpawnLogic() { - return this.impl$performsSpawnLogic; + return this.impl$spongeData.configAdapter().get().world.keepSpawnLoaded; } @Override public void bridge$setPerformsSpawnLogic(final boolean performsSpawnLogic) { - this.impl$performsSpawnLogic = performsSpawnLogic; + this.impl$spongeData.configAdapter().get().world.keepSpawnLoaded = performsSpawnLogic; } @Override public boolean bridge$loadOnStartup() { - return this.impl$loadOnStartup; + return this.impl$spongeData.configAdapter().get().world.loadOnStartup; } @Override public void bridge$setLoadOnStartup(final boolean loadOnStartup) { - this.impl$loadOnStartup = loadOnStartup; + this.impl$spongeData.configAdapter().get().world.loadOnStartup = loadOnStartup; } @Override public Optional bridge$serializationBehavior() { - return Optional.ofNullable(this.impl$serializationBehavior); + return Optional.ofNullable(this.impl$spongeData.configAdapter().get().world.serializationBehavior); } @Override public void bridge$setSerializationBehavior(@Nullable final SerializationBehavior behavior) { - this.impl$serializationBehavior = behavior; + this.impl$spongeData.configAdapter().get().world.serializationBehavior = behavior; } @Override public Optional bridge$displayName() { - return Optional.ofNullable(this.impl$displayName); + return Optional.ofNullable(this.impl$spongeData.configAdapter().get().world.displayName); } @Override public void bridge$setDisplayName(@Nullable final Component displayName) { - this.impl$displayName = displayName; + this.impl$spongeData.configAdapter().get().world.displayName = displayName; } @Override public Optional bridge$viewDistance() { - return Optional.ofNullable(this.impl$viewDistance); + return Optional.ofNullable(this.impl$spongeData.configAdapter().get().world.viewDistance); } @Override public void bridge$setViewDistance(@Nullable final Integer viewDistance) { - this.impl$viewDistance = viewDistance; + this.impl$spongeData.configAdapter().get().world.viewDistance = viewDistance; this.bridge$triggerViewDistanceLogic(); } - @Override public void bridge$populateFromLevelStem(final LevelStem dimension) { - final LevelStemBridge bridge = (LevelStemBridge) (Object) dimension; this.impl$dimensionType = dimension.type().value(); - this.impl$displayName = bridge.bridge$displayName(); + this.impl$chunkGenerator = dimension.generator(); + + // Legacy back compat + final LevelStemBridge bridge = (LevelStemBridge) (Object) dimension; + if (!bridge.bridge$hasLegacyData()) { + return; + } + Optional.ofNullable(bridge.bridge$displayName()).ifPresent(this::bridge$setDisplayName); final Difficulty difficulty = bridge.bridge$difficulty(); final GameType gameType = bridge.bridge$gameMode(); final Boolean isHardcore = bridge.bridge$hardcore(); @@ -215,48 +204,57 @@ public abstract class PrimaryLevelDataMixin implements ServerLevelData, WorldDat if (gameType != null) { this.impl$customGameType = true; } + final var difficultySettings = new LevelSettings.DifficultySettings( + difficulty == null ? this.settings.difficultySettings().difficulty() : difficulty, + isHardcore == null ? this.settings.difficultySettings().hardcore() : isHardcore, + false + ); this.settings = new LevelSettings( - this.settings.levelName(), - gameType == null ? this.settings.gameType() : gameType, - isHardcore == null ? this.settings.hardcore() : isHardcore, - difficulty == null ? this.settings.difficulty() : difficulty, - allowCommands == null ? this.settings.allowCommands() : allowCommands, - this.settings.gameRules(), - this.settings.getDataConfiguration()); + this.settings.levelName(), + gameType == null ? this.settings.gameType() : gameType, + difficultySettings, + allowCommands == null ? this.settings.allowCommands() : allowCommands, + this.settings.dataConfiguration()); final Vector3i spawnPos = bridge.bridge$spawnPosition(); if (spawnPos != null) { - this.shadow$setSpawn(VecHelper.toBlockPos(spawnPos), this.spawnAngle); + this.shadow$setSpawn(new RespawnData(new GlobalPos(this.respawnData.dimension(), VecHelper.toBlockPos(spawnPos)),this.respawnData.pitch(), this.respawnData.yaw())); this.impl$customSpawnPosition = true; } - this.impl$serializationBehavior = bridge.bridge$serializationBehavior(); - this.impl$pvp = bridge.bridge$pvp(); - this.impl$loadOnStartup = bridge.bridge$loadOnStartup(); - this.impl$performsSpawnLogic = bridge.bridge$performsSpawnLogic(); - this.impl$viewDistance = bridge.bridge$viewDistance(); + Optional.ofNullable(bridge.bridge$serializationBehavior()).ifPresent(this::bridge$setSerializationBehavior); + Optional.ofNullable(bridge.bridge$pvp()).ifPresent(this::bridge$setPvp); + this.bridge$setLoadOnStartup(bridge.bridge$loadOnStartup()); + this.bridge$setPerformsSpawnLogic(bridge.bridge$performsSpawnLogic()); + this.bridge$setViewDistance(bridge.bridge$viewDistance()); } @Override public BiMap bridge$getMapUUIDIndex() { - return this.impl$mapUUIDIndex; + return this.impl$getMapUUIDData().mapUUIDIndex(); } @Override public int bridge$getIndexForUniqueId(final UUID uuid) { - final Integer index = this.impl$playerUniqueIdMap.inverse().get(uuid); + final var data = this.impl$getMapUUIDData(); + final Integer index = data.playerUUIDIndex().inverse().get(uuid); if (index != null) { return index; } - final int newIndex = this.impl$playerUniqueIdMap.size(); - this.impl$playerUniqueIdMap.put(newIndex, uuid); + final int newIndex = data.playerUUIDIndex().size(); + data.playerUUIDIndex().put(newIndex, uuid); + data.setDirty(); return newIndex; } @Override public Optional bridge$getUniqueIdForIndex(final int index) { - return Optional.ofNullable(this.impl$playerUniqueIdMap.get(index)); + return Optional.ofNullable(this.impl$getMapUUIDData().playerUUIDIndex().get(index)); + } + + private SpongeMapUUIDData impl$getMapUUIDData() { + return SpongeCommon.server().getDataStorage().computeIfAbsent(SpongeMapUUIDData.TYPE); } void impl$updateWorldForDifficultyChange(final ServerLevel level, final boolean isLocked) { @@ -264,11 +262,11 @@ public abstract class PrimaryLevelDataMixin implements ServerLevelData, WorldDat final Difficulty difficulty = this.getDifficulty(); if (difficulty == Difficulty.HARD) { - level.setSpawnSettings(true, true); + level.setSpawnSettings(true); // set spawn enemies true } else if (server.isSingleplayer()) { - level.setSpawnSettings(difficulty != Difficulty.PEACEFUL, true); + level.setSpawnSettings(difficulty != Difficulty.PEACEFUL); } else { - level.setSpawnSettings(((MinecraftServerAccessor) server).invoker$isSpawningMonsters(), server.isSpawningAnimals()); + level.setSpawnSettings(server.getGameRules().get(GameRules.SPAWN_MONSTERS)); } level.players().forEach(player -> player.connection.send(new ClientboundChangeDifficultyPacket(difficulty, isLocked))); @@ -276,55 +274,27 @@ public abstract class PrimaryLevelDataMixin implements ServerLevelData, WorldDat @Override public void bridge$hardcore(final boolean hardcore) { - ((LevelSettingsAccessor) (Object) this.settings).accessor$hardcore(hardcore); + final var oldDifficulty = this.settings.difficultySettings(); + final var newDifficulty = new LevelSettings.DifficultySettings(oldDifficulty.difficulty(), hardcore, oldDifficulty.locked()); + this.settings = new LevelSettings( + this.settings.levelName(), + this.settings.gameType(), + newDifficulty, + this.settings.allowCommands(), + this.settings.dataConfiguration() + ); } @Override public void bridge$allowCommands(final boolean allowCommands) { - ((LevelSettingsAccessor) (Object) this.settings).accessor$allowCommands(allowCommands); - } - - @Override - @SuppressWarnings("deprecated") - public void bridge$readSpongeLevelData(final Dynamic dynamic) { - dynamic.get(Constants.Sponge.Data.V2.SPONGE_DATA).get().ifSuccess(v2 -> { - v2.get(Constants.Sponge.World.UNIQUE_ID).read(UUIDUtil.CODEC).result().ifPresent(this.impl$spongeData::setUniqueId); - - v2.get(Constants.Map.MAP_UUID_INDEX).readMap(Codec.STRING, UUIDUtil.CODEC).result().ifPresent(value -> { - final BiMap mapIndex = HashBiMap.create(); - for (final Pair pair : value) { - final int id = Integer.parseInt(pair.getFirst()); - mapIndex.put(id, pair.getSecond()); - } - this.impl$mapUUIDIndex = mapIndex; - }); - - // TODO Move this to Schema - v2.get(Constants.Sponge.LEGACY_SPONGE_PLAYER_UUID_TABLE).readList(LegacyUUIDCodec.CODEC).result().orElseGet(() -> - v2.get(Constants.Sponge.SPONGE_PLAYER_UUID_TABLE).readList(UUIDUtil.CODEC).result().orElse(Collections.emptyList()) - ).forEach(uuid -> this.impl$playerUniqueIdMap.inverse().putIfAbsent(uuid, this.impl$playerUniqueIdMap.size())); - }); - - this.data$setCompound((CompoundTag) dynamic.getValue()); - DataUtil.syncTagToData(this); - this.data$setCompound(null); - } - - @Override - public CompoundTag bridge$writeSpongeLevelData() { - final CompoundTag data = new CompoundTag(); - data.putUUID(Constants.Sponge.World.UNIQUE_ID, this.impl$spongeData.uniqueId()); - - // Map Storage - final CompoundTag mapUUIDIndexTag = new CompoundTag(); - MapUtil.saveMapUUIDIndex(mapUUIDIndexTag, this.impl$mapUUIDIndex); - data.put(Constants.Map.MAP_UUID_INDEX, mapUUIDIndexTag); - - final ListTag playerIdList = new ListTag(); - data.put(Constants.Sponge.SPONGE_PLAYER_UUID_TABLE, playerIdList); - this.impl$playerUniqueIdMap.values().forEach(uuid -> playerIdList.add(new IntArrayTag(UUIDUtil.uuidToIntArray(uuid)))); - - return data; + final var oldDifficulty = this.settings.difficultySettings(); + this.settings = new LevelSettings( + this.settings.levelName(), + this.settings.gameType(), + oldDifficulty, + allowCommands, + this.settings.dataConfiguration() + ); } @Override @@ -333,27 +303,12 @@ public String toString() { .add("key=" + this.impl$spongeData.key()) .add("worldType=" + this.impl$dimensionType) .add("uniqueId=" + this.impl$spongeData.uniqueId()) - .add("spawn=" + VecHelper.toVector3i(this.spawnPos)) + .add("spawn=" + this.respawnData.toString()) .add("gameType=" + this.getGameType()) .add("hardcore=" + this.isHardcore()) .add("difficulty=" + this.getDifficulty()) .toString(); } - - @Inject(method = "isRaining", at = @At(value = "HEAD"), cancellable = true) - private void impl$onIsRaining(final CallbackInfoReturnable cir) { - if (this.impl$dimensionType.hasCeiling()) { - cir.setReturnValue(false); - } - } - - @Inject(method = "isThundering", at = @At(value = "HEAD"), cancellable = true) - private void impl$onIsThundering(final CallbackInfoReturnable cir) { - if (this.impl$dimensionType.hasCeiling()) { - cir.setReturnValue(false); - } - } - @Override public CompoundTag data$getCompound() { return this.impl$compound; @@ -363,4 +318,7 @@ public String toString() { public void data$setCompound(final CompoundTag nbt) { this.impl$compound = nbt; } + + // LevelStem persistence for runtime-created worlds is now handled by SpongeWorldManager.createLevel() + // which adds the LevelStem to WorldGenSettings.dimensions() (a SavedData in server-level SavedDataStorage). } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/SavedDataStorageMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/SavedDataStorageMixin.java new file mode 100644 index 00000000000..7e299105340 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/SavedDataStorageMixin.java @@ -0,0 +1,94 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.level.storage; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.RegistryOps; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.saveddata.SavedDataType; +import net.minecraft.world.level.storage.SavedDataStorage; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.bridge.data.DataCompoundHolder; +import org.spongepowered.common.bridge.world.level.storage.DimensionDataStorageBridge; +import org.spongepowered.common.data.DataUtil; + +import java.util.Optional; + +@Mixin(SavedDataStorage.class) +public abstract class SavedDataStorageMixin implements DimensionDataStorageBridge { + + // @formatter:off + private @Nullable Identifier impl$dimensionKey; + // @formatter:on + + @WrapOperation(method = "encodeUnchecked", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/nbt/CompoundTag;put(Ljava/lang/String;Lnet/minecraft/nbt/Tag;)Lnet/minecraft/nbt/Tag;" + ) + ) + public Tag impl$writeAdditionalMapNBT( + final CompoundTag instance, final String key, final Tag encoded, + final Operation original, final SavedDataType type, + final SavedData data, final RegistryOps registry + ) { + if (data instanceof DataCompoundHolder dch) { + if (DataUtil.syncDataToTag(dch)) { + instance.merge(dch.data$getCompound()); + } + } + return original.call(instance, key, encoded); + } + + @WrapOperation(method = "readSavedData", at = @At(value = "INVOKE", target = "Ljava/util/Optional;orElse(Ljava/lang/Object;)Ljava/lang/Object;")) + public T readSpongeMapData( + final Optional instance, final T other, final Operation original, + final @Local CompoundTag rootTag + ) { + final var result = original.call(instance, other); + if (result instanceof DataCompoundHolder dch) { + dch.data$setCompound(rootTag); + DataUtil.syncTagToData(dch); + dch.data$setCompound(null); + } + return result; + } + + @Override + public void bridge$dimensionKey(final @Nullable Identifier dimensionKey) { + this.impl$dimensionKey = dimensionKey; + } + + @Override + public @Nullable Identifier bridge$dimensionKey() { + return this.impl$dimensionKey; + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java index 6e46f440fa7..74e2d04db02 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/scores/PlayerTeamMixin.java @@ -28,10 +28,10 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import net.minecraft.ChatFormatting; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.scores.PlayerTeam; import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.TeamColor; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.Opcodes; @@ -57,7 +57,7 @@ public abstract class PlayerTeamMixin implements PlayerTeamBridge { // @formatter:off @Shadow @Final @Mutable @Nullable private Scoreboard scoreboard; @Shadow private net.minecraft.network.chat.Component displayName; - @Shadow private ChatFormatting color; + @Shadow private Optional color; @Shadow private net.minecraft.network.chat.Component playerPrefix; @Shadow private net.minecraft.network.chat.Component playerSuffix; @Shadow public abstract Collection getPlayers(); @@ -79,7 +79,7 @@ public abstract class PlayerTeamMixin implements PlayerTeamBridge { this.bridge$displayName = LegacyComponentSerializer.legacySection().deserialize(name); this.bridge$prefix = SpongeAdventure.asAdventure(this.playerPrefix); this.bridge$suffix = SpongeAdventure.asAdventure(this.playerSuffix); - this.bridge$color = SpongeAdventure.asAdventureNamed(this.color); + this.bridge$color = this.color.map(SpongeAdventure::asAdventureNamed).orElse(NamedTextColor.WHITE); } @Redirect( @@ -135,8 +135,8 @@ public abstract class PlayerTeamMixin implements PlayerTeamBridge { } @Inject(method = "setColor", at = @At("RETURN")) - private void impl$trackColorChange(final ChatFormatting color, final CallbackInfo ci) { - this.bridge$color = SpongeAdventure.asAdventureNamed(color); + private void impl$trackColorChange(final Optional color, final CallbackInfo ci) { + this.bridge$color = color.map(SpongeAdventure::asAdventureNamed).orElse(NamedTextColor.WHITE); } @Override @@ -183,7 +183,7 @@ public abstract class PlayerTeamMixin implements PlayerTeamBridge { @Override public void bridge$setColor(final NamedTextColor color) { this.bridge$color = color; - this.color = SpongeAdventure.asVanilla(color); + this.color = Optional.ofNullable(SpongeAdventure.asVanillaTeamColor(color)); this.impl$teamChanged(); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/LevelChunkTicksMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/LevelChunkTicksMixin.java index cbb3cea5dc0..0d03d875a25 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/LevelChunkTicksMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/LevelChunkTicksMixin.java @@ -64,15 +64,15 @@ public abstract class LevelChunkTicksMixin implements LevelChunkTicksBridge tick, final CallbackInfo ci) { final ServerLevel level = ((LevelTicksBridge) this.impl$tickList).bridge$level(); - final PhaseContext context = PhaseTracker.getInstance().getPhaseContext(); + final PhaseContext context = PhaseTracker.getWorldInstance(level).getPhaseContext(); context.associateScheduledTickUpdate(level, tick); context.applyOwnerIfAvailable(owner -> ((CreatorTrackedBridge) (Object) tick).tracker$setTrackedUUID(PlayerTracker.Type.CREATOR, owner)); context.applyNotifierIfAvailable(notified -> ((CreatorTrackedBridge) (Object) tick).tracker$setTrackedUUID(PlayerTracker.Type.NOTIFIER, notified)); ((TickNextTickDataBridge) (Object) tick).bridge$createdByList(level, (LevelChunkTicks) (Object) this); } - @ModifyExpressionValue(method = "save(JLjava/util/function/Function;)Lnet/minecraft/nbt/ListTag;", - at = @At(value = "INVOKE", target = "Ljava/util/Queue;iterator()Ljava/util/Iterator;")) + @ModifyExpressionValue(method = "pack(J)Ljava/util/List;", + at = @At(value = "INVOKE", target = "Ljava/util/List;iterator()Ljava/util/Iterator;")) private Iterator> impl$onSaveSkipCancelled(final Iterator> original) { return Iterators.filter(original, t -> ((TickNextTickDataBridge) (Object) t).bridge$internalState() != ScheduledUpdate.State.CANCELLED); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/SavedTickMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/SavedTickMixin.java index c9a740a5a52..32a8c386887 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/SavedTickMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/SavedTickMixin.java @@ -24,21 +24,29 @@ */ package org.spongepowered.common.mixin.core.world.ticks; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.mojang.datafixers.Products; +import com.mojang.datafixers.kinds.App; +import com.mojang.datafixers.kinds.Applicative; +import com.mojang.datafixers.util.Function4; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.component.CustomData; import net.minecraft.world.ticks.SavedTick; -import net.minecraft.world.ticks.ScheduledTick; +import net.minecraft.world.ticks.TickPriority; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.accessor.world.item.component.CustomDataAccessor; import org.spongepowered.common.bridge.CreatorTrackedBridge; import org.spongepowered.common.bridge.data.DataCompoundHolder; import org.spongepowered.common.data.DataUtil; import org.spongepowered.common.data.holder.SpongeMutableDataHolder; import java.util.Optional; -import java.util.function.Function; @Mixin(SavedTick.class) public abstract class SavedTickMixin implements SpongeMutableDataHolder, DataCompoundHolder, CreatorTrackedBridge { @@ -55,26 +63,39 @@ public abstract class SavedTickMixin implements SpongeMutableDataHolder, Data this.impl$compound = nbt; } - @Inject(method = "saveTick(Lnet/minecraft/world/ticks/ScheduledTick;Ljava/util/function/Function;J)Lnet/minecraft/nbt/CompoundTag;", at = @At("RETURN")) - private static void impl$onSaveTick(final ScheduledTick $$0, final Function $$1, final long $$2, final CallbackInfoReturnable cir) { - if (DataUtil.syncDataToTag($$0)) { - cir.getReturnValue().merge(((DataCompoundHolder) (Object) $$0).data$getCompound()); - } - } - - @Inject(method = "save", at = @At("RETURN")) - private void impl$onSaveTick(final CallbackInfoReturnable cir) { - if (DataUtil.syncDataToTag(this)) { - cir.getReturnValue().merge(this.data$getCompound()); - } - } - - @Inject(method = "loadTick", at = @At("RETURN")) - private static void impl$onLoad(final CompoundTag $$0, final Function> $$1, final CallbackInfoReturnable>> cir) { - cir.getReturnValue().ifPresent(tick -> { - ((DataCompoundHolder) (Object) tick).data$setCompound($$0); - DataUtil.syncTagToData(tick); - ((DataCompoundHolder) (Object) tick).data$setCompound(null); + /** + * @return The codec + * @author gabizou - 25w07a - February 15th, 2025 + * @reason Because SavedTicks are now saved via Codec, we have to modify the codec to inject our + * custom data alongside it. This means that we're explicitly not going to be calling the original + * operation, but merely adding the existing products and instantiation function. It is why we use + * a {@link Redirect} instead of a {@link WrapOperation}. Unless there is a better way to modify + * the codec to support the data field additions. + */ + @SuppressWarnings("deprecation") + @Redirect(method = "lambda$codec$1", at = @At(value = "INVOKE", target = "Lcom/mojang/datafixers/Products$P4;apply(Lcom/mojang/datafixers/kinds/Applicative;Lcom/mojang/datafixers/util/Function4;)Lcom/mojang/datafixers/kinds/App;")) + private static App>, SavedTick> lambda$codec$1( + final Products.P4>, T, BlockPos, Integer, TickPriority> products, + final Applicative>, ?> instance, + final Function4> function + ) { + final RecordCodecBuilder, Optional> spongeCustomData = Codec.optionalField("customData", CustomData.CODEC, true).forGetter(t -> { + final var bridge = ((SavedTickMixin) (Object) t); + DataUtil.syncDataToTag(bridge); + if (bridge.impl$compound != null) { + return Optional.of(CustomData.of(bridge.impl$compound)); + } + return Optional.empty(); }); + return products.and(spongeCustomData) + .apply(instance, (t, blockPos, integer, tickPriority, customData) -> { + final var tick = function.apply(t, blockPos, integer, tickPriority); + customData.ifPresent(data -> { + ((DataCompoundHolder) (Object) tick).data$setCompound(((CustomDataAccessor) (Object) data).accessor$tag()); + DataUtil.syncTagToData(tick); + ((DataCompoundHolder) (Object) tick).data$setCompound(null); + }); + return tick; + }); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/ScheduledTickMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/ScheduledTickMixin.java index 499b875ae0b..43910a70f03 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/ScheduledTickMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/ticks/ScheduledTickMixin.java @@ -30,6 +30,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.ticks.LevelChunkTicks; +import net.minecraft.world.ticks.SavedTick; import net.minecraft.world.ticks.ScheduledTick; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -39,9 +40,13 @@ import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.accessor.world.ticks.LevelChunkTicksAccessor; import org.spongepowered.common.bridge.CreatorTrackedBridge; import org.spongepowered.common.bridge.data.DataCompoundHolder; +import org.spongepowered.common.bridge.data.SpongeDataHolderBridge; import org.spongepowered.common.bridge.world.ticks.TickNextTickDataBridge; import org.spongepowered.common.data.holder.SpongeMutableDataHolder; import org.spongepowered.common.util.Preconditions; @@ -115,4 +120,9 @@ public abstract class ScheduledTickMixin implements TickNextTickDataBridge public void data$setCompound(final CompoundTag nbt) { this.impl$compound = nbt; } + + @Inject(method = "toSavedTick", at = @At(value = "RETURN")) + private void impl$onToSaveSkipCancelled(final long $$0, final CallbackInfoReturnable> cir) { + ((SpongeDataHolderBridge) (Object) cir.getReturnValue()).bridge$mergeDeserialized(((SpongeDataHolderBridge) this).bridge$getManipulator()); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/entityactivation/entity/projectile/AbstractArrowEntityMixin_EntityActivation.java b/src/mixins/java/org/spongepowered/common/mixin/entityactivation/entity/projectile/arrow/AbstractArrowEntityMixin_EntityActivation.java similarity index 95% rename from src/mixins/java/org/spongepowered/common/mixin/entityactivation/entity/projectile/AbstractArrowEntityMixin_EntityActivation.java rename to src/mixins/java/org/spongepowered/common/mixin/entityactivation/entity/projectile/arrow/AbstractArrowEntityMixin_EntityActivation.java index c0bdbb05971..602bccdb92a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/entityactivation/entity/projectile/AbstractArrowEntityMixin_EntityActivation.java +++ b/src/mixins/java/org/spongepowered/common/mixin/entityactivation/entity/projectile/arrow/AbstractArrowEntityMixin_EntityActivation.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.entityactivation.entity.projectile; +package org.spongepowered.common.mixin.entityactivation.entity.projectile.arrow; -import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.entity.projectile.arrow.AbstractArrow; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.mixin.entityactivation.entity.EntityMixin_EntityActivation; diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_AdapterInventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_AdapterInventory_API.java index 978c6686b2e..099aaf6baa8 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_AdapterInventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_AdapterInventory_API.java @@ -26,12 +26,13 @@ import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.decoration.Mannequin; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.block.entity.CampfireBlockEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.inventory.adapter.impl.DefaultImplementedAdapterInventory; -@Mixin({ArmorStand.class, Mob.class, Player.class, CampfireBlockEntity.class}) +@Mixin({ArmorStand.class, Mannequin.class, Mob.class, Player.class, CampfireBlockEntity.class}) public abstract class TraitMixin_AdapterInventory_API implements DefaultImplementedAdapterInventory.WithClear { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_ArmorEquipable_Inventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_ArmorEquipable_Inventory_API.java index 66569883a51..0e441cbd09a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_ArmorEquipable_Inventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_ArmorEquipable_Inventory_API.java @@ -28,6 +28,7 @@ import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.decoration.Mannequin; import net.minecraft.world.entity.player.Player; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.type.HandType; @@ -49,7 +50,7 @@ import java.util.Optional; // All living implementors of ArmorEquipable -@Mixin({ArmorStand.class, Mob.class, Player.class}) +@Mixin({ArmorStand.class, Mannequin.class, Mob.class, Player.class}) public abstract class TraitMixin_ArmorEquipable_Inventory_API implements ArmorEquipable { // TODO can we implement canEquip? diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_Viewable_Inventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_Viewable_Inventory_API.java index 5d5e2f794fa..a1b2efe37c7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_Viewable_Inventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/TraitMixin_Viewable_Inventory_API.java @@ -26,10 +26,10 @@ import net.minecraft.world.CompoundContainer; import net.minecraft.world.Container; -import net.minecraft.world.entity.npc.AbstractVillager; import net.minecraft.world.entity.npc.ClientSideMerchant; +import net.minecraft.world.entity.npc.villager.AbstractVillager; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.vehicle.AbstractMinecartContainer; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecartContainer; import net.minecraft.world.inventory.PlayerEnderChestContainer; import net.minecraft.world.level.block.entity.BaseContainerBlockEntity; import net.minecraft.world.level.block.entity.LecternBlockEntity; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/SavedDataMixin.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/EquipmentSlotGroupMixin_Inventory_API.java similarity index 53% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/level/SavedDataMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/EquipmentSlotGroupMixin_Inventory_API.java index cada3864214..4f15254dc48 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/SavedDataMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/EquipmentSlotGroupMixin_Inventory_API.java @@ -22,32 +22,43 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.level; +package org.spongepowered.common.mixin.inventory.api.world.entity; -import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.level.saveddata.SavedData; +import net.kyori.adventure.text.Component; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.EquipmentSlotGroup; +import org.spongepowered.api.item.inventory.equipment.EquipmentCondition; +import org.spongepowered.api.item.inventory.equipment.EquipmentType; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import org.spongepowered.common.bridge.data.DataCompoundHolder; -import org.spongepowered.common.data.DataUtil; - -import java.io.File; - -@Mixin(SavedData.class) -public abstract class SavedDataMixin { - - @Inject(method = "save(Ljava/io/File;Lnet/minecraft/core/HolderLookup$Provider;)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/saveddata/SavedData;save(Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/core/HolderLookup$Provider;)Lnet/minecraft/nbt/CompoundTag;"), - locals = LocalCapture.CAPTURE_FAILHARD) - public void impl$writeAdditionalMapNBT(File file, HolderLookup.Provider registry, final CallbackInfo cir, CompoundTag compound) { - if (this instanceof DataCompoundHolder) { - if (DataUtil.syncDataToTag(this)) { - compound.merge(((DataCompoundHolder) this).data$getCompound()); - } - } + +import java.util.Objects; + +@Mixin(EquipmentSlotGroup.class) +public abstract class EquipmentSlotGroupMixin_Inventory_API implements EquipmentCondition { + + @Shadow public abstract boolean shadow$test(EquipmentSlot slot); + + @Shadow @Final private String key; + + private Component api$component; + + @Inject(method = "(Ljava/lang/String;IILjava/lang/String;Ljava/util/function/Predicate;)V", at = @At("RETURN")) + private void api$setComponent(final CallbackInfo ci) { + this.api$component = Component.translatable("item.modifiers." + this.key); + } + + @Override + public Component asComponent() { + return this.api$component; + } + + @Override + public boolean test(final EquipmentType type) { + return this.shadow$test((EquipmentSlot) (Object) Objects.requireNonNull(type, "type")); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/EquipmentSlotMixin_Inventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/EquipmentSlotMixin_Inventory_API.java index f9ad70efbd4..1e5310f9c6c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/EquipmentSlotMixin_Inventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/EquipmentSlotMixin_Inventory_API.java @@ -25,6 +25,8 @@ package org.spongepowered.common.mixin.inventory.api.world.entity; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.EquipmentSlotGroup; +import org.spongepowered.api.item.inventory.equipment.EquipmentCondition; import org.spongepowered.api.item.inventory.equipment.EquipmentGroup; import org.spongepowered.api.item.inventory.equipment.EquipmentType; import org.spongepowered.asm.mixin.Final; @@ -40,4 +42,9 @@ public abstract class EquipmentSlotMixin_Inventory_API implements EquipmentType public EquipmentGroup group() { return (EquipmentGroup) (Object) this.type; } + + @Override + public EquipmentCondition condition() { + return (EquipmentCondition) (Object) EquipmentSlotGroup.bySlot((EquipmentSlot) (Object) this); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/animal/horse/AbstractHorseMixin_Carrier_Inventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/animal/equine/AbstractHorseMixin_Carrier_Inventory_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/animal/horse/AbstractHorseMixin_Carrier_Inventory_API.java rename to src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/animal/equine/AbstractHorseMixin_Carrier_Inventory_API.java index 3e0ab03763a..5088f481a98 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/animal/horse/AbstractHorseMixin_Carrier_Inventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/animal/equine/AbstractHorseMixin_Carrier_Inventory_API.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.inventory.api.world.entity.animal.horse; +package org.spongepowered.common.mixin.inventory.api.world.entity.animal.equine; import net.minecraft.world.SimpleContainer; -import net.minecraft.world.entity.animal.horse.AbstractHorse; +import net.minecraft.world.entity.animal.equine.AbstractHorse; import org.spongepowered.api.item.inventory.Carrier; import org.spongepowered.api.item.inventory.type.CarriedInventory; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/monster/PillagerMixin_Carrier_Inventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/monster/illager/PillagerMixin_Carrier_Inventory_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/monster/PillagerMixin_Carrier_Inventory_API.java rename to src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/monster/illager/PillagerMixin_Carrier_Inventory_API.java index 7b3da8a4f4c..b631114f78e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/monster/PillagerMixin_Carrier_Inventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/monster/illager/PillagerMixin_Carrier_Inventory_API.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.inventory.api.world.entity.monster; +package org.spongepowered.common.mixin.inventory.api.world.entity.monster.illager; import net.minecraft.world.SimpleContainer; -import net.minecraft.world.entity.monster.Pillager; +import net.minecraft.world.entity.monster.illager.Pillager; import org.spongepowered.api.item.inventory.Carrier; import org.spongepowered.api.item.inventory.type.CarriedInventory; import org.spongepowered.asm.mixin.Final; diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/npc/AbstractVillagerMixin_Carrier_Inventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/npc/villager/AbstractVillagerMixin_Carrier_Inventory_API.java similarity index 96% rename from src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/npc/AbstractVillagerMixin_Carrier_Inventory_API.java rename to src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/npc/villager/AbstractVillagerMixin_Carrier_Inventory_API.java index 31ebb802dae..9a80f099b20 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/npc/AbstractVillagerMixin_Carrier_Inventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/npc/villager/AbstractVillagerMixin_Carrier_Inventory_API.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.inventory.api.world.entity.npc; +package org.spongepowered.common.mixin.inventory.api.world.entity.npc.villager; import net.minecraft.world.SimpleContainer; -import net.minecraft.world.entity.npc.AbstractVillager; +import net.minecraft.world.entity.npc.villager.AbstractVillager; import org.spongepowered.api.item.inventory.Carrier; import org.spongepowered.api.item.inventory.type.CarriedInventory; import org.spongepowered.asm.mixin.Final; diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/AbstractMinecartContainerMixin_Carried_Inventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/minecart/AbstractMinecartContainerMixin_Carried_Inventory_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/AbstractMinecartContainerMixin_Carried_Inventory_API.java rename to src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/minecart/AbstractMinecartContainerMixin_Carried_Inventory_API.java index 66aea21b821..59686c3b50d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/AbstractMinecartContainerMixin_Carried_Inventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/minecart/AbstractMinecartContainerMixin_Carried_Inventory_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.inventory.api.world.entity.vehicle; +package org.spongepowered.common.mixin.inventory.api.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.AbstractMinecartContainer; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecartContainer; import org.spongepowered.api.entity.vehicle.minecart.carrier.CarrierMinecart; import org.spongepowered.api.item.inventory.type.CarriedInventory; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/AbstractMinecartContainerMixin_Carrier_Inventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/minecart/AbstractMinecartContainerMixin_Carrier_Inventory_API.java similarity index 94% rename from src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/AbstractMinecartContainerMixin_Carrier_Inventory_API.java rename to src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/minecart/AbstractMinecartContainerMixin_Carrier_Inventory_API.java index c770d7e904c..dace7bfd2e5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/AbstractMinecartContainerMixin_Carrier_Inventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/entity/vehicle/minecart/AbstractMinecartContainerMixin_Carrier_Inventory_API.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.inventory.api.world.entity.vehicle; +package org.spongepowered.common.mixin.inventory.api.world.entity.vehicle.minecart; -import net.minecraft.world.entity.vehicle.AbstractMinecartContainer; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecartContainer; import org.spongepowered.api.entity.vehicle.minecart.carrier.CarrierMinecart; import org.spongepowered.api.item.inventory.Carrier; import org.spongepowered.api.item.inventory.type.CarriedInventory; diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/ArmorSlot_Inventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/ArmorSlot_Inventory_API.java index 08c8b1d6515..9ff6feb1088 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/ArmorSlot_Inventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/ArmorSlot_Inventory_API.java @@ -24,9 +24,10 @@ */ package org.spongepowered.common.mixin.inventory.api.world.inventory; +import net.minecraft.world.inventory.ArmorSlot; import org.spongepowered.api.item.inventory.slot.EquipmentSlot; import org.spongepowered.asm.mixin.Mixin; -@Mixin(targets = "net/minecraft/world/inventory/ArmorSlot") +@Mixin(ArmorSlot.class) public abstract class ArmorSlot_Inventory_API implements EquipmentSlot { } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/CrafterBlockBlockEntityMixin_Inventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/CrafterBlockBlockEntityMixin_Inventory_API.java index a0ca6cfd4ed..38482b14627 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/CrafterBlockBlockEntityMixin_Inventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/CrafterBlockBlockEntityMixin_Inventory_API.java @@ -70,14 +70,17 @@ public GridInventory asGrid() { */ @Override public boolean craftItem() { + if (!(this.level instanceof ServerLevel sl)) { + return false; + } final var input = ((CraftingContainer) this).asCraftInput(); - final var potentialResults = CrafterBlock.getPotentialResults(this.level, input); + final var potentialResults = CrafterBlock.getPotentialResults(sl, input); if (potentialResults.isEmpty()) { return false; } final var recipeHolder = potentialResults.get(); final var recipe = recipeHolder.value(); - final var craftedStack = recipe.assemble(input, this.level.registryAccess()); + final var craftedStack = recipe.assemble(input); if (craftedStack.isEmpty()) { return false; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/SlotMixin_Inventory_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/SlotMixin_Inventory_API.java index 16790079ee4..132c5f30982 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/SlotMixin_Inventory_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/api/world/inventory/SlotMixin_Inventory_API.java @@ -77,7 +77,7 @@ public InventoryTransactionResult set(ItemStackLike stack) { remaining -= push; if (remaining > 0) { - result.reject(ItemStackUtil.cloneDefensive(nativeStack, remaining)); + result.reject((ItemStackLike) ItemStackUtil.cloneDefensive(nativeStack, remaining)); } return result.build(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/entity/LivingEntityMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/entity/LivingEntityMixin_Inventory.java index c1efdd8a2fb..c007045332f 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/entity/LivingEntityMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/entity/LivingEntityMixin_Inventory.java @@ -24,6 +24,8 @@ */ package org.spongepowered.common.mixin.inventory.event.entity; +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EquipmentSlot; @@ -37,6 +39,7 @@ import org.spongepowered.api.item.inventory.ItemStackSnapshot; import org.spongepowered.api.item.inventory.Slot; import org.spongepowered.api.item.inventory.equipment.EquipmentType; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -59,40 +62,44 @@ public abstract class LivingEntityMixin_Inventory extends Entity { // @formatter:off + @Shadow @Final private Map lastEquipmentItems; + @Shadow public abstract void shadow$setItemSlot(EquipmentSlot slotIn, ItemStack stack); - @Shadow protected abstract ItemStack shadow$getLastHandItem(EquipmentSlot p_241347_1_); - @Shadow protected abstract ItemStack shadow$getLastArmorItem(EquipmentSlot p_241346_1_); @Shadow protected abstract void shadow$completeUsingItem(); - - @Shadow private ItemStack lastBodyItemStack; // @formatter:on + protected LivingEntityMixin_Inventory(final EntityType param0, final Level param1) { super(param0, param1); } + + @WrapMethod(method = "setItemSlot") + protected void inventory$wrapSetSlotWithTransaction(EquipmentSlot slot, ItemStack item, Operation original) { + original.call(slot, item); + } + + @Inject(method = "handleHandSwap", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, - at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;broadcast(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/network/protocol/Packet;)V")) + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;sendToTrackingPlayers(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/network/protocol/Packet;)V")) protected void inventory$onHandleHandSwap(final Map map, final CallbackInfo ci) { final Slot mainHand = this.impl$getSpongeSlot(EquipmentSlot.MAINHAND); - final boolean customMainHand = this.impl$throwEquipmentEvent(EquipmentSlot.MAINHAND, mainHand, map.get(EquipmentSlot.MAINHAND), this.shadow$getLastHandItem(EquipmentSlot.MAINHAND)); + final var lastMainHand = this.lastEquipmentItems.get(EquipmentSlot.MAINHAND); + final boolean customMainHand = this.impl$throwEquipmentEvent(EquipmentSlot.MAINHAND, mainHand, map.get(EquipmentSlot.MAINHAND), lastMainHand); + final var lastOffHand = this.lastEquipmentItems.get(EquipmentSlot.OFFHAND); final Slot offHand = this.impl$getSpongeSlot(EquipmentSlot.OFFHAND); - final boolean customOffHand = this.impl$throwEquipmentEvent(EquipmentSlot.OFFHAND, offHand, map.get(EquipmentSlot.OFFHAND), this.shadow$getLastHandItem(EquipmentSlot.OFFHAND)); + final boolean customOffHand = this.impl$throwEquipmentEvent(EquipmentSlot.OFFHAND, offHand, map.get(EquipmentSlot.OFFHAND), lastOffHand); if (customMainHand || customOffHand) { ci.cancel(); // If canceled or customized let handleEquipmentChanges send packets instead } } @Inject(method = "handleEquipmentChanges", - at = @At(value = "INVOKE", remap = false, target = "Ljava/util/Map;forEach(Ljava/util/function/BiConsumer;)V")) + at = @At(value = "INVOKE", target = "Ljava/util/Map;forEach(Ljava/util/function/BiConsumer;)V")) protected void inventory$onHandleEquipmentChanges(final Map map, final CallbackInfo ci) { map.entrySet().forEach(entry -> { final Slot slotAdapter = this.impl$getSpongeSlot(entry.getKey()); - final ItemStack oldStack = switch (entry.getKey().getType()) { - case HAND -> this.shadow$getLastHandItem(entry.getKey()); - case HUMANOID_ARMOR -> this.shadow$getLastArmorItem(entry.getKey()); - case ANIMAL_ARMOR -> this.lastBodyItemStack; - }; + final ItemStack oldStack = this.lastEquipmentItems.get(entry.getKey()); entry.setValue(this.impl$callEquipmentEvent(entry.getKey(), slotAdapter, entry.getValue(), oldStack)); }); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/entity/player/PlayerMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/entity/player/PlayerMixin_Inventory.java index f946a609a84..27af0116a21 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/entity/player/PlayerMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/entity/player/PlayerMixin_Inventory.java @@ -36,7 +36,6 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.mixin.inventory.event.entity.LivingEntityMixin_Inventory; @@ -70,13 +69,6 @@ protected PlayerMixin_Inventory(final EntityType param0, final Level param1) super.inventory$onHandleEquipmentChanges(map, ci); } - @Inject(method = "setItemSlot", at = @At(value = "HEAD")) - protected void impl$beforeSetItemSlot(final EquipmentSlot param0, final ItemStack param1, final CallbackInfo ci) { - } - - @Inject(method = "setItemSlot", at = @At(value = "RETURN")) - protected void impl$afterSetItemSlot(final EquipmentSlot param0, final ItemStack param1, final CallbackInfo ci) { - } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/inventory/container/EnchantmentContainerMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/inventory/container/EnchantmentContainerMixin_Inventory.java index e3a0315872f..808d416d160 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/inventory/container/EnchantmentContainerMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/inventory/container/EnchantmentContainerMixin_Inventory.java @@ -85,15 +85,15 @@ public abstract class EnchantmentContainerMixin_Inventory { } // enchantItem lambda - @Inject(method = "lambda$clickMenuButton$1", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;onEnchantmentPerformed(Lnet/minecraft/world/item/ItemStack;I)V"), require = 1) + @Inject(method = "lambda$clickMenuButton$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;onEnchantmentPerformed(Lnet/minecraft/world/item/ItemStack;I)V"), require = 1) private void impl$beforeEnchantItem(final CallbackInfo ci) { this.prevItem = ItemStackUtil.snapshotOf(this.enchantSlots.getItem(0)); this.prevLapis = ItemStackUtil.snapshotOf(this.enchantSlots.getItem(1)); } // enchantItem lambda - @Inject(method = "lambda$clickMenuButton$1", cancellable = true, - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;awardStat(Lnet/minecraft/resources/ResourceLocation;)V"), require = 1) + @Inject(method = "lambda$clickMenuButton$0", cancellable = true, + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;awardStat(Lnet/minecraft/resources/Identifier;)V"), require = 1) private void impl$afterEnchantItem(final ItemStack itemstack, final int id, final Player playerIn, final int i, final ItemStack itemstack1, final Level arg5, final BlockPos arg6, final CallbackInfo ci) { final ItemStackSnapshot newItem = ItemStackUtil.snapshotOf(this.enchantSlots.getItem(0)); final ItemStackSnapshot newLapis = ItemStackUtil.snapshotOf(this.enchantSlots.getItem(1)); diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/level/ServerPlayerMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/level/ServerPlayerMixin_Inventory.java index ab4b5d1911f..5b2270743fa 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/level/ServerPlayerMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/level/ServerPlayerMixin_Inventory.java @@ -29,8 +29,9 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Cancellable; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientboundHorseScreenOpenPacket; +import net.minecraft.network.protocol.game.ClientboundMountScreenOpenPacket; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.world.Container; @@ -39,7 +40,7 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.animal.horse.AbstractHorse; +import net.minecraft.world.entity.animal.equine.AbstractHorse; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -61,7 +62,6 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.world.entity.EntityBridge; import org.spongepowered.common.bridge.world.entity.player.PlayerInventoryBridge; @@ -83,7 +83,6 @@ import org.spongepowered.common.item.util.ItemStackUtil; import org.spongepowered.common.mixin.inventory.event.entity.player.PlayerMixin_Inventory; -import java.util.Collections; import java.util.Map; @Mixin(ServerPlayer.class) @@ -93,6 +92,8 @@ public abstract class ServerPlayerMixin_Inventory extends PlayerMixin_Inventory @Shadow public ServerGamePacketListenerImpl connection; @Nullable private EffectTransactor inventory$effectTransactor = null; @Nullable private Object inventory$menuProvider; + + @Shadow public abstract ServerLevel shadow$level(); // @formatter:on // Ignore @@ -105,34 +106,31 @@ public abstract class ServerPlayerMixin_Inventory extends PlayerMixin_Inventory // -- Overrides from PlayerMixin_Inventory @Override - protected void impl$beforeSetItemSlot(final EquipmentSlot param0, final ItemStack param1, final CallbackInfo ci) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + protected void inventory$wrapSetSlotWithTransaction(EquipmentSlot slot, ItemStack item, Operation original) { + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); final PlayerInventoryTransaction.EventCreator eventCreator = context.getState() instanceof SwapHandItemsState ? - PlayerInventoryTransaction.EventCreator.SWAP_HAND : PlayerInventoryTransaction.EventCreator.STANDARD; + PlayerInventoryTransaction.EventCreator.SWAP_HAND : PlayerInventoryTransaction.EventCreator.STANDARD; this.inventory$effectTransactor = transactor.logPlayerInventoryChangeWithEffect((Player) (Object) this, eventCreator); - } - - @Override - protected void impl$afterSetItemSlot(final EquipmentSlot param0, final ItemStack param1, final CallbackInfo ci) { try (final EffectTransactor ignored = this.inventory$effectTransactor) { + original.call(slot, item); this.inventoryMenu.broadcastChanges(); // for capture } finally { this.inventory$effectTransactor = null; } } - @Inject(method = "drop(Z)Z", + @Inject(method = "drop(Z)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Inventory;removeFromSelected(Z)Lnet/minecraft/world/item/ItemStack;")) - protected void impl$beforeRemoveItem(final boolean param0, final CallbackInfoReturnable cir) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + protected void impl$beforeRemoveItem(final boolean param0, final CallbackInfo cir) { + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); this.inventory$effectTransactor = transactor.logDropFromPlayerInventory((ServerPlayer) (Object) this, param0); } - @Inject(method = "drop(Z)Z", + @Inject(method = "drop(Z)V", at = @At(value = "RETURN")) - protected void impl$onPlayerDrop(final boolean param0, final CallbackInfoReturnable cir) { + protected void impl$onPlayerDrop(final boolean param0, final CallbackInfo cir) { try (final EffectTransactor ignored = this.inventory$effectTransactor) { this.containerMenu.broadcastChanges(); // for capture } finally { @@ -140,7 +138,6 @@ public abstract class ServerPlayerMixin_Inventory extends PlayerMixin_Inventory } } - @Override protected void inventory$onTouch(final Entity entity, final Player player) { if (entity instanceof ItemEntity) { entity.playerTouch(player); // ItemEntityMixin_Inventory creates transactions for pickup event @@ -150,7 +147,7 @@ public abstract class ServerPlayerMixin_Inventory extends PlayerMixin_Inventory entity.playerTouch(player); return; } - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext(); try (final EffectTransactor ignored = context.getTransactor().logPlayerInventoryChangeWithEffect(player, PlayerInventoryTransaction.EventCreator.STANDARD)) { entity.playerTouch(player); this.inventoryMenu.broadcastChanges(); // capture @@ -165,7 +162,7 @@ public abstract class ServerPlayerMixin_Inventory extends PlayerMixin_Inventory @Override protected void inventory$onUpdateUsingItem(final LivingEntity thisPlayer) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); try (final EffectTransactor ignored = transactor.logPlayerInventoryChangeWithEffect((ServerPlayer) (Object) this, PlayerInventoryTransaction.EventCreator.STANDARD)) { this.shadow$completeUsingItem(); @@ -184,7 +181,7 @@ public abstract class ServerPlayerMixin_Inventory extends PlayerMixin_Inventory ) ) private void impl$afterOpenHorseInventory(final AbstractHorse $$0, final Container $$1, final CallbackInfo ci) { - PhaseTracker.SERVER.getPhaseContext() + PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext() .getTransactor() .logContainerSet((ServerPlayer) (Object) this); } @@ -217,7 +214,7 @@ public abstract class ServerPlayerMixin_Inventory extends PlayerMixin_Inventory method = "openHorseInventory", at = @At( value = "NEW", - target = "(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/Container;Lnet/minecraft/world/entity/animal/horse/AbstractHorse;I)Lnet/minecraft/world/inventory/HorseInventoryMenu;" + target = "(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/Container;Lnet/minecraft/world/entity/animal/equine/AbstractHorse;I)Lnet/minecraft/world/inventory/HorseInventoryMenu;" ) ) private HorseInventoryMenu impl$transactHorseInventoryMenuCreationWithEffect( @@ -229,7 +226,7 @@ public abstract class ServerPlayerMixin_Inventory extends PlayerMixin_Inventory ci.cancel(); return menu; } - this.connection.send(new ClientboundHorseScreenOpenPacket(menu.containerId, $$4, $$3.getId())); + this.connection.send(new ClientboundMountScreenOpenPacket(menu.containerId, $$4, $$3.getId())); return menu; } @@ -259,12 +256,12 @@ public abstract class ServerPlayerMixin_Inventory extends PlayerMixin_Inventory @WrapMethod(method = "doCloseContainer") private void impl$onDoCloseContainer(final Operation original) { - final PhaseTracker tracker = PhaseTracker.SERVER; + final PhaseTracker tracker = PhaseTracker.getWorldInstance(this.shadow$level()); final ItemStackSnapshot resultingCursor = ItemStackUtil.snapshotOf(this.containerMenu.getCarried()); final Transaction cursorTransaction = new Transaction<>(resultingCursor, resultingCursor); - final InteractContainerEvent.Close event = SpongeEventFactory.createInteractContainerEventClose( + final InteractContainerEvent.Close event = SpongeEventFactory.createInteractContainerEventClosePre( tracker.currentCause(), (org.spongepowered.api.item.inventory.Container) this.containerMenu, - cursorTransaction, (Inventory) this.containerMenu, Collections.emptyList()); + cursorTransaction); SpongeCommon.post(event); PacketPhaseUtil.handleCursorRestore((ServerPlayer) (Object) this, event.cursorTransaction(), event.isCancelled()); @@ -302,10 +299,10 @@ public abstract class ServerPlayerMixin_Inventory extends PlayerMixin_Inventory @WrapOperation(method = "doCloseContainer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/AbstractContainerMenu;removed(Lnet/minecraft/world/entity/player/Player;)V")) private void impl$onDoCloseContainerCaptureRemoval(final AbstractContainerMenu instance, final Player player, final Operation original) { - final PhaseTracker tracker = PhaseTracker.SERVER; + final PhaseTracker tracker = PhaseTracker.getWorldInstance(this.shadow$level()); final PhaseContext<@NonNull ?> context = tracker.getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); - try (final EffectTransactor ignored = transactor.logPlayerInventoryChangeWithEffect(player, PlayerInventoryTransaction.EventCreator.STANDARD)) { + try (final EffectTransactor ignored = transactor.logCloseInventory(context, player)) { original.call(instance, player); instance.broadcastChanges(); TrackingUtil.processBlockCaptures(context); diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/level/ServerPlayerMixin_Shared_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/level/ServerPlayerMixin_Shared_Inventory.java index 34a5f4ce2a1..78341d434a5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/level/ServerPlayerMixin_Shared_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/level/ServerPlayerMixin_Shared_Inventory.java @@ -27,6 +27,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Cancellable; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.EntityType; @@ -36,6 +37,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -55,9 +57,11 @@ public abstract class ServerPlayerMixin_Shared_Inventory extends PlayerMixin_Inventory { // @formatter:off - @Nullable private Object inventory$menuProvider; + @Shadow public abstract ServerLevel shadow$level(); // @formatter:on + @Nullable private Object inventory$menuProvider; + protected ServerPlayerMixin_Shared_Inventory(final EntityType param0, final Level param1) { super(param0, param1); } @@ -72,7 +76,7 @@ protected ServerPlayerMixin_Shared_Inventory(final EntityType param0, final L ) ) private void impl$afterOpenMenu(final CallbackInfoReturnable cir) { - PhaseTracker.SERVER.getPhaseContext().getTransactor().logContainerSet((ServerPlayer) (Object) this); + PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext().getTransactor().logContainerSet((ServerPlayer) (Object) this); } @Inject(method = "openMenu", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;initMenu(Lnet/minecraft/world/inventory/AbstractContainerMenu;)V")) @@ -98,7 +102,7 @@ protected ServerPlayerMixin_Shared_Inventory(final EntityType param0, final L final MenuProvider menuProvider, final int containerCounter, final net.minecraft.world.entity.player.Inventory inventory, final Player player, final Operation original, final @Cancellable CallbackInfoReturnable cir ) { - final PhaseContext context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext context = PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext(); try (final EffectTransactor ignored = context.getTransactor().logOpenInventory((ServerPlayer) (Object) this)) { final AbstractContainerMenu menu = original.call(menuProvider, containerCounter, inventory, player); context.containerLocation().ifPresent(loc -> { diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/network/ServerGamePacketListenerImplMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/network/ServerGamePacketListenerImplMixin_Inventory.java index a2a40e02b91..41ae443347e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/network/ServerGamePacketListenerImplMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/server/network/ServerGamePacketListenerImplMixin_Inventory.java @@ -26,6 +26,7 @@ import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.network.protocol.game.ServerboundContainerClickPacket; import net.minecraft.network.protocol.game.ServerboundSelectTradePacket; import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket; @@ -38,6 +39,7 @@ import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.inventory.MerchantMenu; import net.minecraft.world.item.ItemStack; @@ -51,13 +53,13 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.bridge.world.TrackedWorldBridge; import org.spongepowered.common.bridge.world.inventory.AbstractContainerMenu_InventoryBridge; import org.spongepowered.common.bridge.world.inventory.container.MenuBridge; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; import org.spongepowered.common.event.tracking.context.transaction.TransactionalCaptureSupplier; -import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; import org.spongepowered.common.inventory.custom.SpongeInventoryMenu; import org.spongepowered.common.item.util.ItemStackUtil; @@ -69,7 +71,7 @@ public class ServerGamePacketListenerImplMixin_Inventory { @Redirect(method = "handleSetCreativeModeSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/InventoryMenu;broadcastChanges()V")) private void impl$onBroadcastCreativeActionResult(final InventoryMenu inventoryMenu, final ServerboundSetCreativeModeSlotPacket packetIn) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); final ItemStack itemstack = packetIn.itemStack(); @@ -79,10 +81,19 @@ public class ServerGamePacketListenerImplMixin_Inventory { inventoryMenu.broadcastChanges(); } + @WrapOperation(method = "tryPickItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/InventoryMenu;broadcastChanges()V")) + private void impl$onPickItem(final InventoryMenu menu, final Operation original) { + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.level()).getPhaseContext(); + final TransactionalCaptureSupplier transactor = context.getTransactor(); + try (final EffectTransactor ignored = transactor.logClickContainer(menu, this.player.getInventory().getSelectedSlot(), 0, ContainerInput.PICKUP, this.player)) { + original.call(menu); + } + } + @Redirect(method = "handleSetCreativeModeSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;drop(Lnet/minecraft/world/item/ItemStack;Z)Lnet/minecraft/world/entity/item/ItemEntity;")) private ItemEntity impl$onBroadcastCreativeActionResult(final ServerPlayer serverPlayer, final ItemStack stack, final boolean param1) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); try (final EffectTransactor ignored = transactor.logCreativeClickContainer(-1, ItemStackUtil.snapshotOf(stack), this.player)) { return serverPlayer.drop(stack, param1); @@ -95,7 +106,7 @@ public class ServerGamePacketListenerImplMixin_Inventory { slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;stopUsingItem()V")) ) private void impl$onHandleSetCarriedItem(final ServerboundSetCarriedItemPacket packet, final CallbackInfo ci) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); final int slotIdx = packet.getSlot(); transactor.logPlayerCarriedItem(this.player, slotIdx); @@ -104,15 +115,15 @@ public class ServerGamePacketListenerImplMixin_Inventory { @Redirect(method = "handleContainerClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/AbstractContainerMenu;sendAllDataToRemote()V")) private void impl$onSpectatorClick(final AbstractContainerMenu menu, final ServerboundContainerClickPacket packet) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); - try (final EffectTransactor ignored = transactor.logClickContainer(menu, packet.getSlotNum(), packet.getButtonNum(), packet.getClickType(), + try (final EffectTransactor ignored = transactor.logClickContainer(menu, packet.slotNum(), packet.buttonNum(), packet.containerInput(), this.player )) { if (menu instanceof MenuBridge bridge) { final SpongeInventoryMenu spongeMenu = bridge.bridge$getMenu(); if (spongeMenu != null) { - spongeMenu.onClick(packet.getSlotNum(), packet.getButtonNum(), packet.getClickType(), this.player, ((Container) menu)); + spongeMenu.onClick(packet.slotNum(), packet.buttonNum(), packet.containerInput(), this.player, ((Container) menu)); } } menu.sendAllDataToRemote(); @@ -134,20 +145,18 @@ public class ServerGamePacketListenerImplMixin_Inventory { at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayerGameMode;useItem(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResult;")) private InteractionResult impl$onHandleUseItem(final ServerPlayerGameMode serverPlayerGameMode, final ServerPlayer param0, final Level param1, final ItemStack param2, final InteractionHand param3) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); - try (final EffectTransactor ignored = transactor.logPlayerInventoryChangeWithEffect(this.player, PlayerInventoryTransaction.EventCreator.STANDARD)) { - final InteractionResult result = serverPlayerGameMode.useItem(param0, param1, param2, param3); - this.player.inventoryMenu.broadcastChanges(); // capture - return result; + try (final EffectTransactor ignored = transactor.logSecondaryInteractItemTransaction(param0, param2)) { + final var pipeline = ((TrackedWorldBridge) param1).bridge$startItemInteractionUseChange(param1, param0, param3, param2); + return pipeline.processInteraction(context); } - // TrackingUtil.processBlockCaptures called by UseItemPacketState } @Redirect(method = "handleRenameItem(Lnet/minecraft/network/protocol/game/ServerboundRenameItemPacket;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/AnvilMenu;setItemName(Ljava/lang/String;)Z")) private boolean impl$onHandleRenameItem(final AnvilMenu menu, final String name) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); try (final var ignored = transactor.logIgnoredInventory(this.player.containerMenu)) { return menu.setItemName(name); @@ -157,7 +166,7 @@ public class ServerGamePacketListenerImplMixin_Inventory { @Inject(method = "handleSelectTrade", at = @At("RETURN")) private void impl$onHandleSelectTrade(final ServerboundSelectTradePacket param0, final CallbackInfo ci) { if (this.player.containerMenu instanceof MerchantMenu) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); transactor.logSelectTrade(this.player, param0.getItem()); this.player.containerMenu.broadcastChanges(); // capture diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/AbstractContainerMenuMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/AbstractContainerMenuMixin_Inventory.java index b0578090087..b1e98c9658a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/AbstractContainerMenuMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/AbstractContainerMenuMixin_Inventory.java @@ -25,14 +25,16 @@ package org.spongepowered.common.mixin.inventory.event.world.inventory; import net.minecraft.core.NonNullList; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.inventory.ContainerListener; import net.minecraft.world.inventory.ContainerSynchronizer; import net.minecraft.world.inventory.DataSlot; +import net.minecraft.world.inventory.RemoteSlot; import net.minecraft.world.inventory.ResultSlot; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; @@ -77,10 +79,10 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon @Final @Shadow private List containerListeners; @Shadow private boolean suppressRemoteUpdates; @Final @Shadow private List dataSlots; - @Shadow @Final private NonNullList remoteSlots; + @Shadow @Final private NonNullList remoteSlots; @Shadow @Nullable private ContainerSynchronizer synchronizer; - @Shadow protected abstract void shadow$doClick(int param0, int param1, ClickType param2, Player param3); + @Shadow protected abstract void shadow$doClick(int param0, int param1, ContainerInput param2, Player param3); @Shadow protected abstract void shadow$updateDataSlotListeners(int $$0, int $$1); @Shadow protected abstract void shadow$synchronizeDataSlotToRemote(int $$0, int $$1); @Shadow protected abstract void shadow$synchronizeCarriedToRemote(); @@ -111,15 +113,15 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon ), slice = @Slice( from = @At(value = "FIELD", - target = "Lnet/minecraft/world/inventory/ClickType;SWAP:Lnet/minecraft/world/inventory/ClickType;" + target = "Lnet/minecraft/world/inventory/ContainerInput;SWAP:Lnet/minecraft/world/inventory/ContainerInput;" ), to = @At(value = "FIELD", - target = "Lnet/minecraft/world/inventory/ClickType;CLONE:Lnet/minecraft/world/inventory/ClickType;" + target = "Lnet/minecraft/world/inventory/ContainerInput;CLONE:Lnet/minecraft/world/inventory/ContainerInput;" ) ) ) private void impl$handleUnviewedSlotSwap(final Inventory inv, final int index, final ItemStack newStack) { - if (!PhaseTracker.SERVER.onSidedThread() || inv.player.inventoryMenu == inv.player.containerMenu || Inventory.isHotbarSlot(index)) { + if (!PhaseTracker.getWorldInstance().onSidedThread() || inv.player.inventoryMenu == inv.player.containerMenu || Inventory.isHotbarSlot(index)) { inv.setItem(index, newStack); } else { final ItemStackSnapshot oldItem = ItemStackUtil.snapshotOf(inv.getItem(index)); @@ -135,16 +137,16 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon ), slice = @Slice( from = @At(value = "FIELD", - target = "Lnet/minecraft/world/inventory/ClickType;SWAP:Lnet/minecraft/world/inventory/ClickType;" + target = "Lnet/minecraft/world/inventory/ContainerInput;SWAP:Lnet/minecraft/world/inventory/ContainerInput;" ), to = @At(value = "FIELD", - target = "Lnet/minecraft/world/inventory/ClickType;CLONE:Lnet/minecraft/world/inventory/ClickType;" + target = "Lnet/minecraft/world/inventory/ContainerInput;CLONE:Lnet/minecraft/world/inventory/ContainerInput;" ) ) ) - private ItemStack impl$handleUnviewedSlotSwap2(final ItemStack origin, final int splitOff, int $$0, int index, ClickType $$2, Player $$3) { + private ItemStack impl$handleUnviewedSlotSwap2(final ItemStack origin, final int splitOff, int $$0, int index, ContainerInput $$2, Player $$3) { Inventory inv = $$3.getInventory(); - if (!PhaseTracker.SERVER.onSidedThread() || inv.player.inventoryMenu == inv.player.containerMenu || Inventory.isHotbarSlot(index)) { + if (!PhaseTracker.getWorldInstance().onSidedThread() || inv.player.inventoryMenu == inv.player.containerMenu || Inventory.isHotbarSlot(index)) { return origin.split(splitOff); } else { final ItemStackSnapshot oldItem = ItemStackUtil.snapshotOf(origin); @@ -163,10 +165,10 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon ), slice = @Slice( from = @At(value = "FIELD", - target = "Lnet/minecraft/world/inventory/ClickType;THROW:Lnet/minecraft/world/inventory/ClickType;" + target = "Lnet/minecraft/world/inventory/ContainerInput;THROW:Lnet/minecraft/world/inventory/ContainerInput;" ), to = @At(value = "FIELD", - target = "Lnet/minecraft/world/inventory/ClickType;PICKUP_ALL:Lnet/minecraft/world/inventory/ClickType;" + target = "Lnet/minecraft/world/inventory/ContainerInput;PICKUP_ALL:Lnet/minecraft/world/inventory/ContainerInput;" ) ) ) @@ -189,11 +191,11 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon private ItemStack impl$transferStackInSlot(final AbstractContainerMenu thisContainer, final Player player, final int slotId) { final ItemStack result = thisContainer.quickMoveStack(player, slotId); // Crafting on Serverside? - if (((LevelBridge) player.level()).bridge$isFake() || player.level().isClientSide || !(thisContainer.getSlot(slotId) instanceof ResultSlot)) { + if (((LevelBridge) player.level()).bridge$isFake() || player.level().isClientSide() || !(thisContainer.getSlot(slotId) instanceof ResultSlot)) { return result; } this.bridge$detectAndSendChanges(true, true); - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance((ServerLevel) player.level()).getPhaseContext(); TrackingUtil.processBlockCaptures(context); // ClickContainerEvent -> CraftEvent -> PreviewEvent // result is modified by the ClickMenuTransaction return result; @@ -202,17 +204,17 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon @Redirect( method = "clicked", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/inventory/AbstractContainerMenu;doClick(IILnet/minecraft/world/inventory/ClickType;Lnet/minecraft/world/entity/player/Player;)V")) + target = "Lnet/minecraft/world/inventory/AbstractContainerMenu;doClick(IILnet/minecraft/world/inventory/ContainerInput;Lnet/minecraft/world/entity/player/Player;)V")) private void inventory$wrapDoClickWithTransaction( final AbstractContainerMenu menu, final int slotId, final int dragType, - final ClickType clickType, + final ContainerInput clickType, final Player player ) { if (((LevelBridge) player.level()).bridge$isFake() || player.level().isClientSide()) { this.shadow$doClick(slotId, dragType, clickType, player); return; } - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance((ServerLevel) player.level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); try (final EffectTransactor ignored = transactor.logClickContainer(menu, slotId, dragType, clickType, player)) { this.shadow$doClick(slotId, dragType, clickType, player); @@ -224,7 +226,7 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon @Inject(method = "broadcastFullState", at = @At("HEAD"), cancellable = true) private void impl$broadcastFullStateWithTransactions(final CallbackInfo ci) { - if (!PhaseTracker.SERVER.onSidedThread()) { + if (!PhaseTracker.getWorldInstance().onSidedThread()) { return; } this.bridge$detectAndSendChanges(false, false); @@ -245,7 +247,7 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon */ @Inject(method = "broadcastChanges", at = @At("HEAD"), cancellable = true) private void impl$broadcastChangesWithTransactions(final CallbackInfo ci) { - if (!PhaseTracker.SERVER.onSidedThread()) { + if (!PhaseTracker.getWorldInstance().onSidedThread()) { return; } this.bridge$detectAndSendChanges(false, true); @@ -258,7 +260,7 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon public void bridge$detectAndSendChanges(final boolean capture, final boolean synchronize) { // Code-Flow changed from vanilla completely! - final PhaseContext phaseContext = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext phaseContext = PhaseTracker.getWorldInstance().getPhaseContext(); if (phaseContext.captureModifiedContainer((AbstractContainerMenu) (Object) this)) { return; } @@ -303,9 +305,9 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon listener.slotChanged(((AbstractContainerMenu) (Object) this), i, oldStack); } if (synchronize) { - final ItemStack remoteStack = this.remoteSlots.get(i); - if (!ItemStack.matches(remoteStack, newStack)) { - this.remoteSlots.set(i, newStack.copy()); + final RemoteSlot remoteStack = this.remoteSlots.get(i); + if (!remoteStack.matches(oldStack)) { + remoteStack.force(oldStack); if (this.synchronizer != null) { this.synchronizer.sendSlotChange(((AbstractContainerMenu) (Object) this), i, newStack); } @@ -347,8 +349,9 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon } private void impl$capture(final Integer index, final ItemStack newStack, final ItemStack oldStack) { - final PhaseContext phaseContext = PhaseTracker.SERVER.getPhaseContext(); - if (PhaseTracker.SERVER.onSidedThread() && + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(); + final PhaseContext phaseContext = phaseTracker.getPhaseContext(); + if (phaseTracker.onSidedThread() && !(phaseContext.isRestoring() // do not capture when block restoring & initial sync on inventory open || phaseContext instanceof TileEntityTickContext)) { // do not capture for open inventories when ticking BlockEntities final ItemStackSnapshot oldItem = ItemStackUtil.snapshotOf(oldStack); @@ -364,7 +367,7 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon } private void impl$captureSwap(int index, Inventory inv, ItemStackSnapshot oldItem, ItemStackSnapshot newItem) { - final PhaseContext<@NonNull ?> phaseContext = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> phaseContext = PhaseTracker.getWorldInstance().getPhaseContext(); final TransactionalCaptureSupplier transactor = phaseContext.getTransactor(); final org.spongepowered.api.item.inventory.Slot adapter = ((InventoryAdapter) inv.player.getInventory()).inventoryAdapter$getSlot(index).get(); final SlotTransaction newTransaction = new SlotTransaction(adapter, oldItem, newItem); diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/AnvilMenuMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/AnvilMenuMixin_Inventory.java index 2d5f4c7aa4e..a081fce9f6a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/AnvilMenuMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/AnvilMenuMixin_Inventory.java @@ -49,7 +49,7 @@ public abstract class AnvilMenuMixin_Inventory extends ItemCombinerMenuMixin_Inv @Inject(method = "createResult", at = @At(value = "RETURN")) private void impl$throwUpdateAnvilEvent(final CallbackInfo ci) { - if (!ShouldFire.UPDATE_ANVIL_EVENT || !PhaseTracker.SERVER.onSidedThread()) { + if (!ShouldFire.UPDATE_ANVIL_EVENT || !PhaseTracker.getWorldInstance().onSidedThread()) { return; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/CraftingMenuMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/CraftingMenuMixin_Inventory.java index bf57192d81a..4e7bf87dcb6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/CraftingMenuMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/CraftingMenuMixin_Inventory.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.mixin.inventory.event.world.inventory; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -31,7 +32,6 @@ import net.minecraft.world.inventory.CraftingMenu; import net.minecraft.world.inventory.ResultContainer; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.item.inventory.Inventory; import org.spongepowered.api.item.inventory.crafting.CraftingInventory; @@ -50,8 +50,10 @@ public abstract class CraftingMenuMixin_Inventory { // Crafting Preview @Redirect(method = "slotChangedCraftingGrid", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/ResultContainer;setItem(ILnet/minecraft/world/item/ItemStack;)V")) - private static void beforeSlotChangedCraftingGrid(final ResultContainer resultContainer, final int slotId, final ItemStack itemStack, - final AbstractContainerMenu param0, final Level param1, final Player player, final CraftingContainer craftingContainer, final ResultContainer resultcontainer) { + private static void beforeSlotChangedCraftingGrid( + final ResultContainer resultContainer, final int slotId, final ItemStack itemStack, + final AbstractContainerMenu param0, final ServerLevel param1, final Player player, + final CraftingContainer craftingContainer, final ResultContainer resultcontainer) { resultContainer.setItem(slotId, itemStack); final Inventory inv = ((Inventory) player.containerMenu).query(QueryTypes.INVENTORY_TYPE.get().of(CraftingInventory.class)); @@ -60,7 +62,7 @@ private static void beforeSlotChangedCraftingGrid(final ResultContainer resultCo return; } - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance().getPhaseContext(); if (!context.isRestoring()) { final TransactionalCaptureSupplier transactor = context.getTransactor(); transactor.logCraftingPreview((ServerPlayer) player, (CraftingInventory) inv, craftingContainer); diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/ResultSlotMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/ResultSlotMixin_Inventory.java index 65ac8c43f99..80c4c55a145 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/ResultSlotMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/ResultSlotMixin_Inventory.java @@ -26,6 +26,7 @@ import net.minecraft.core.NonNullList; import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; @@ -33,11 +34,11 @@ import net.minecraft.world.inventory.ResultSlot; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.CraftingInput; import net.minecraft.world.item.crafting.RecipeHolder; -import net.minecraft.world.item.crafting.RecipeInput; import net.minecraft.world.item.crafting.RecipeManager; import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.item.inventory.Inventory; @@ -64,6 +65,10 @@ public abstract class ResultSlotMixin_Inventory extends Slot { @Shadow @Final private net.minecraft.world.inventory.CraftingContainer craftSlots; + @Shadow private NonNullList shadow$getRemainingItems(CraftingInput input, Level level) { + throw new IllegalStateException("Untransformed shadow method!"); + } + public ResultSlotMixin_Inventory(final Container inventoryIn, final int index, final int xPosition, final int yPosition) { super(inventoryIn, index, xPosition, yPosition); } @@ -87,8 +92,11 @@ public void set(@Nullable final ItemStack stack) { @Inject(method = "onTake", at = @At("HEAD")) private void impl$beforeTake(final Player thePlayer, final ItemStack stack, final CallbackInfo ci) { + if (!(thePlayer.level() instanceof ServerLevel sl)) { + return; + } if (this.impl$onTakeRecipe == null || !this.impl$onTakeRecipe.value().matches(this.craftSlots.asCraftInput(), thePlayer.level())) { - final RecipeManager manager = thePlayer.level().getRecipeManager(); + final RecipeManager manager = sl.recipeAccess(); this.impl$onTakeRecipe = manager.getRecipeFor(RecipeType.CRAFTING, this.craftSlots.asCraftInput(), thePlayer.level()).orElse(null); } @@ -105,12 +113,12 @@ public void set(@Nullable final ItemStack stack) { stack.shrink(1); } - @Redirect(method = "onTake", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/crafting/RecipeManager;getRemainingItemsFor(Lnet/minecraft/world/item/crafting/RecipeType;Lnet/minecraft/world/item/crafting/RecipeInput;Lnet/minecraft/world/level/Level;)Lnet/minecraft/core/NonNullList;")) - private > NonNullList impl$onGetRemainingItems(final RecipeManager recipeManager, final RecipeType recipeTypeIn, final I recipeInput, final net.minecraft.world.level.Level worldIn) { + @Redirect(method = "onTake", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/ResultSlot;getRemainingItems(Lnet/minecraft/world/item/crafting/CraftingInput;Lnet/minecraft/world/level/Level;)Lnet/minecraft/core/NonNullList;")) + private NonNullList impl$onGetRemainingItems(ResultSlot instance, CraftingInput $$0, Level $$1) { if (this.impl$onTakeRecipe == null) { - return NonNullList.withSize(recipeInput.size(), ItemStack.EMPTY); + return NonNullList.withSize($$0.size(), ItemStack.EMPTY); } - return worldIn.getRecipeManager().getRemainingItemsFor(recipeTypeIn, recipeInput, worldIn); + return this.shadow$getRemainingItems($$0, $$1); } @Inject(method = "onTake", cancellable = true, at = @At("RETURN")) @@ -126,7 +134,7 @@ public void set(@Nullable final ItemStack stack) { return; } - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance().getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); transactor.logCrafting(thePlayer, this.impl$craftedStack, (CraftingInventory) craftInv, this.impl$onTakeRecipe); diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/SlotMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/SlotMixin_Inventory.java index db84f046d84..f71309dd8f3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/SlotMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/inventory/SlotMixin_Inventory.java @@ -47,8 +47,9 @@ public abstract class SlotMixin_Inventory { @Inject(method = "onQuickCraft(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)V", at = @At("HEAD")) private void inventory$onQuickCraft(final ItemStack slotStack, final ItemStack stackTaken, final CallbackInfo ci) { - if (PhaseTracker.SERVER.onSidedThread()) { - final PhaseContext<@NonNull ?> ctx = PhaseTracker.SERVER.getPhaseContext(); + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(); + if (phaseTracker.onSidedThread()) { + final PhaseContext<@NonNull ?> ctx = phaseTracker.getPhaseContext(); ctx.getTransactor().logShiftCraftingResult((Slot) (Object) this, stackTaken); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/level/block/DropperBlockMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/level/block/DropperBlockMixin_Inventory.java index fa567e29bd6..2ddc535b2ef 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/level/block/DropperBlockMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/level/block/DropperBlockMixin_Inventory.java @@ -24,25 +24,22 @@ */ package org.spongepowered.common.mixin.inventory.event.world.level.block; -import net.minecraft.core.BlockPos; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; import net.minecraft.core.Direction; -import net.minecraft.core.dispenser.BlockSource; -import net.minecraft.server.level.ServerLevel; import net.minecraft.world.Container; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.DispenserBlock; import net.minecraft.world.level.block.DropperBlock; import net.minecraft.world.level.block.entity.DispenserBlockEntity; -import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.api.item.inventory.Inventory; import org.spongepowered.api.item.inventory.transaction.SlotTransaction; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Surrogate; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import org.spongepowered.common.accessor.world.level.block.entity.HopperBlockEntityAccessor; import org.spongepowered.common.bridge.world.inventory.container.TrackedInventoryBridge; import org.spongepowered.common.event.ShouldFire; import org.spongepowered.common.event.inventory.InventoryEventFactory; @@ -51,69 +48,44 @@ @Mixin(DropperBlock.class) public abstract class DropperBlockMixin_Inventory { - @Inject(method = "dispenseFrom", cancellable = true, locals = LocalCapture.CAPTURE_FAILEXCEPTION, - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/DispenserBlockEntity;setItem(ILnet/minecraft/world/item/ItemStack;)V")) - private void impl$afterDispense(final ServerLevel worldIn, final BlockState state, final BlockPos pos, final CallbackInfo callbackInfo, - final DispenserBlockEntity dispensertileentity, final BlockSource proxyblocksource, final int i, final ItemStack itemstack, - final Direction direction, final Container iinventory, final ItemStack itemstack1) { - // after setItem - dispensertileentity.setItem(i, itemstack1); - - if (ShouldFire.TRANSFER_INVENTORY_EVENT_POST) { - // Transfer worked if remainder is one less than the original stack - if (itemstack1.getCount() == itemstack.getCount() - 1) { - final TrackedInventoryBridge capture = InventoryUtil.forCapture(dispensertileentity); - final Inventory sourceInv = ((Inventory) dispensertileentity); - final SlotTransaction sourceSlotTransaction = InventoryEventFactory.captureTransaction(capture, sourceInv, i, itemstack); - InventoryEventFactory.callTransferPost(capture, sourceInv, ((Inventory) iinventory), itemstack, sourceSlotTransaction); - } + @WrapOperation(method = "dispenseFrom", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;" + )) + private ItemStack impl$beforeDispense( + final Container dispenser, final Container container, final ItemStack stackToInsert, final Direction direction, + final Operation original, @Share("container") LocalRef containerRef + ) { + if (ShouldFire.TRANSFER_INVENTORY_EVENT_PRE && InventoryEventFactory.callTransferPre(((Inventory) dispenser), ((Inventory) container)).isCancelled()) { + return stackToInsert; } - - InventoryUtil.updateInventoryNoEvents(dispensertileentity); - InventoryUtil.updateInventoryNoEvents(iinventory); - - // dont call setItem twice - callbackInfo.cancel(); + containerRef.set(container); + return original.call(dispenser, container, stackToInsert, direction); } - @Surrogate - private void afterDispense(final ServerLevel worldIn, final BlockState state, final BlockPos pos, final CallbackInfo callbackInfo, - final DispenserBlockEntity dispensertileentity, final BlockSource proxyblocksource, final int i, final ItemStack itemstack, - final ItemStack itemstack1) { - // after setItem - dispensertileentity.setItem(i, itemstack1); - - final Direction enumfacing = worldIn.getBlockState(pos).getValue(DispenserBlock.FACING); - final BlockPos blockpos = pos.relative(enumfacing); - final Container iinventory = HopperBlockEntityAccessor.invoker$getContainerAt(worldIn, blockpos); - - if (ShouldFire.TRANSFER_INVENTORY_EVENT_POST) { + @Inject(method = "dispenseFrom", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/entity/DispenserBlockEntity;setItem(ILnet/minecraft/world/item/ItemStack;)V", + shift = At.Shift.AFTER + )) + private void impl$afterDispense( + final CallbackInfo ci, @Local final DispenserBlockEntity dispenser, @Local final int index, + @Local(ordinal = 0) final ItemStack stack, @Local(ordinal = 1) final ItemStack remainingStack, + @Share("container") final LocalRef containerRef + ) { + final Container container = containerRef.get(); + if (ShouldFire.TRANSFER_INVENTORY_EVENT_POST && container != null) { // Transfer worked if remainder is one less than the original stack - if (itemstack1.getCount() == itemstack.getCount() - 1) { - final TrackedInventoryBridge capture = InventoryUtil.forCapture(dispensertileentity); - final Inventory sourceInv = ((Inventory) dispensertileentity); - final SlotTransaction sourceSlotTransaction = InventoryEventFactory.captureTransaction(capture, sourceInv, i, itemstack); - InventoryEventFactory.callTransferPost(capture, sourceInv, ((Inventory) iinventory), itemstack, sourceSlotTransaction); + if (remainingStack.getCount() == stack.getCount() - 1) { + final TrackedInventoryBridge capture = InventoryUtil.forCapture(dispenser); + final SlotTransaction sourceSlotTransaction = InventoryEventFactory.captureTransaction(capture, (Inventory) dispenser, index, stack); + InventoryEventFactory.callTransferPost(capture, (Inventory) dispenser, (Inventory) container, stack, sourceSlotTransaction); } } - InventoryUtil.updateInventoryNoEvents(dispensertileentity); - InventoryUtil.updateInventoryNoEvents(iinventory); - - // dont call setInventorySlotContents twice - callbackInfo.cancel(); - } - - @Inject(method = "dispenseFrom", cancellable = true, locals = LocalCapture.CAPTURE_FAILEXCEPTION, - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;")) - private void onDispense(final ServerLevel world, final BlockState state, final BlockPos pos, final CallbackInfo ci, - final DispenserBlockEntity dispensertileentity, final BlockSource proxyblocksource, final int i, final ItemStack itemstack, - final Direction direction, final Container iinventory) { - // Before putStackInInventoryAllSlots - if (InventoryEventFactory.callTransferPre(((Inventory) dispensertileentity), ((Inventory) iinventory)).isCancelled()) { - ci.cancel(); + InventoryUtil.updateInventoryNoEvents(dispenser); + if (container != null) { + InventoryUtil.updateInventoryNoEvents(container); } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory.java index bfaa6605e88..35d39ffa590 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory.java @@ -24,6 +24,11 @@ */ package org.spongepowered.common.mixin.inventory.event.world.level.block.entity; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.Container; import net.minecraft.world.entity.item.ItemEntity; @@ -38,7 +43,6 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import org.spongepowered.common.accessor.world.level.block.entity.HopperBlockEntityAccessor; import org.spongepowered.common.bridge.world.inventory.container.TrackedInventoryBridge; import org.spongepowered.common.event.ShouldFire; import org.spongepowered.common.event.inventory.InventoryEventFactory; @@ -50,78 +54,100 @@ @Mixin(HopperBlockEntity.class) public abstract class HopperBlockEntityMixin_Inventory { - + @Redirect(method = "addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/entity/item/ItemEntity;)Z", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;")) + private static ItemStack impl$transferFromEntity( + final Container sourceNull, final Container destination, final ItemStack stack, final Direction directionNull, @Local(argsOnly = true) final ItemEntity entity) { + return InventoryEventFactory.callHopperInventoryPickupEvent(destination, entity, stack); + } // Call PreEvents - @Redirect(method = "suckInItems", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;getSlots(Lnet/minecraft/world/Container;Lnet/minecraft/core/Direction;)[I")) - private static int[] impl$throwTransferPreIfNotEmpty(final Container inventory, final Direction facing, final Level level, final Hopper hopper) { - final var slots = HopperBlockEntityAccessor.invoker$getSlots(inventory, facing); - final boolean isEmpty = IntStream.of(slots).allMatch(slot -> inventory.getItem(slot).isEmpty()); - if (isEmpty || !ShouldFire.TRANSFER_INVENTORY_EVENT_PRE) { + @ModifyExpressionValue(method = "suckInItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;getSlots(Lnet/minecraft/world/Container;Lnet/minecraft/core/Direction;)[I")) + private static int[] impl$beforeTransferFrom(int[] slots, @Local(argsOnly = true) final Hopper hopper, @Local final Container container) { + final boolean empty = IntStream.of(slots).allMatch(slot -> container.getItem(slot).isEmpty()); + if (empty || !ShouldFire.TRANSFER_INVENTORY_EVENT_PRE) { return slots; } - if (InventoryEventFactory.callTransferPre(InventoryUtil.toInventory(inventory), InventoryUtil.toInventory(hopper)).isCancelled()) { - return new int[]{}; + if (InventoryEventFactory.callTransferPre(InventoryUtil.toInventory(container), InventoryUtil.toInventory(hopper)).isCancelled()) { + return new int[0]; } return slots; } + @ModifyExpressionValue(method = "ejectItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;isFullContainer(Lnet/minecraft/world/Container;Lnet/minecraft/core/Direction;)Z")) + private static boolean impl$beforeTransferTo(boolean full, @Local(argsOnly = true) final HopperBlockEntity hopper, @Local final Container container) { + if (full || !ShouldFire.TRANSFER_INVENTORY_EVENT_PRE) { + return full; + } + return InventoryEventFactory.callTransferPre(InventoryUtil.toInventory(hopper), InventoryUtil.toInventory(container)).isCancelled(); + } + // Capture Transactions - @Redirect(method = "addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;tryMoveInItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;ILnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;")) - private static ItemStack impl$throwEventsForInsertion(final Container source, final Container destination, final ItemStack stack, - final int index, final Direction direction) { - // capture Transaction + @WrapOperation(method = "addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;tryMoveInItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;ILnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;")) + private static ItemStack impl$onTransfer( + final Container source, final Container destination, final ItemStack stack, final int index, final Direction direction, final Operation original + ) { if (!((source instanceof TrackedInventoryBridge || destination instanceof TrackedInventoryBridge) && destination instanceof InventoryAdapter)) { - return HopperBlockEntityAccessor.invoker$tryMoveInItem(source, destination, stack, index, direction); + return original.call(source, destination, stack, index, direction); } if (!ShouldFire.TRANSFER_INVENTORY_EVENT_POST) { - return HopperBlockEntityAccessor.invoker$tryMoveInItem(source, destination, stack, index, direction); + return original.call(source, destination, stack, index, direction); } - TrackedInventoryBridge captureIn = InventoryUtil.forCapture(source); - if (captureIn == null) { - captureIn = InventoryUtil.forCapture(destination); + TrackedInventoryBridge capture = InventoryUtil.forCapture(source); + if (capture == null) { + capture = InventoryUtil.forCapture(destination); } - return InventoryEventFactory.captureTransaction(captureIn, InventoryUtil.toInventory(destination), index, - () -> HopperBlockEntityAccessor.invoker$tryMoveInItem(source, destination, stack, index, direction)); + return InventoryEventFactory.captureTransaction(capture, InventoryUtil.toInventory(destination), index, + () -> original.call(source, destination, stack, index, direction)); } // Post Captured Transactions - @Inject(method = "tryTakeInItemFromSlot", - locals = LocalCapture.CAPTURE_FAILEXCEPTION, - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z", - ordinal = 1)) - private static void imlp$throwTransferEventsWhenPullingItems(final Hopper hopper, final Container iInventory, final int index, - final Direction direction, - final CallbackInfoReturnable cir, final ItemStack itemStack, final int oldCount, final ItemStack itemstack2) { - // after putStackInInventoryAllSlots if the transfer worked - if (ShouldFire.TRANSFER_INVENTORY_EVENT_POST && itemstack2.isEmpty()) { + @Inject(method = "tryTakeInItemFromSlot", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z", ordinal = 1)) + private static void impl$afterTransferFrom( + final Hopper hopper, final Container container, final int index, final Direction direction, + final CallbackInfoReturnable cir, final ItemStack stack, final int originalCount, final ItemStack insertedStack + ) { + // If the transfer worked + if (ShouldFire.TRANSFER_INVENTORY_EVENT_POST && insertedStack.isEmpty()) { // Capture Insert in Origin final TrackedInventoryBridge capture = InventoryUtil.forCapture(hopper); - final int newCount = itemStack.getCount(); - itemstack2.setCount(oldCount); - var itemStack1 = itemstack2.copy(); - itemstack2.setCount(newCount); - final SlotTransaction sourceSlotTransaction = InventoryEventFactory.captureTransaction(capture, InventoryUtil.toInventory(iInventory), index, itemStack1); + final int newCount = stack.getCount(); + stack.setCount(originalCount); + final ItemStack originalStack = stack.copy(); + stack.setCount(newCount); + final SlotTransaction sourceSlotTransaction = InventoryEventFactory.captureTransaction(capture, InventoryUtil.toInventory(container), index, originalStack); // Call event - InventoryEventFactory.callTransferPost(capture, InventoryUtil.toInventory(iInventory), InventoryUtil.toInventory(hopper), itemStack1, sourceSlotTransaction); + InventoryEventFactory.callTransferPost(capture, InventoryUtil.toInventory(container), InventoryUtil.toInventory(hopper), originalStack, sourceSlotTransaction); } - InventoryUtil.updateInventoryNoEvents(iInventory); + InventoryUtil.updateInventoryNoEvents(container); InventoryUtil.updateInventoryNoEvents(hopper); } - @Redirect(method = "addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/entity/item/ItemEntity;)Z", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;")) - private static ItemStack impl$onPutStackInInventoryAllSlots(final Container source, final Container destination, final ItemStack stack, final Direction direction, final Container d2, final ItemEntity entity) { - return InventoryEventFactory.callHopperInventoryPickupEvent(destination, entity, stack); + @Inject(method = "ejectItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z", ordinal = 1)) + private static void impl$afterTransferTo( + final Level level, final BlockPos pos, final HopperBlockEntity hopper, final CallbackInfoReturnable cir, + @Local final Container container, @Local(ordinal = 0) final int index, @Local(ordinal = 1) final int originalCount, + @Local(ordinal = 0) final ItemStack stack, @Local(ordinal = 1) final ItemStack insertedStack + ) { + // If the transfer worked + if (ShouldFire.TRANSFER_INVENTORY_EVENT_POST && insertedStack.isEmpty()) { + // Capture Insert in Origin + final TrackedInventoryBridge capture = InventoryUtil.forCapture(hopper); + final int newCount = stack.getCount(); + stack.setCount(originalCount); + final ItemStack originalStack = stack.copy(); + stack.setCount(newCount); + final SlotTransaction sourceSlotTransaction = InventoryEventFactory.captureTransaction(capture, InventoryUtil.toInventory(hopper), index, originalStack); + // Call event + InventoryEventFactory.callTransferPost(capture, InventoryUtil.toInventory(hopper), InventoryUtil.toInventory(container), originalStack, sourceSlotTransaction); + } + + InventoryUtil.updateInventoryNoEvents(hopper); + InventoryUtil.updateInventoryNoEvents(container); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_ContainerBridge_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_ContainerBridge_Inventory.java index 240d7dbafec..36727641425 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_ContainerBridge_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_ContainerBridge_Inventory.java @@ -27,6 +27,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AbstractFurnaceMenu; +import net.minecraft.world.inventory.AbstractMountInventoryMenu; import net.minecraft.world.inventory.BeaconMenu; import net.minecraft.world.inventory.BrewingStandMenu; import net.minecraft.world.inventory.ChestMenu; @@ -34,7 +35,6 @@ import net.minecraft.world.inventory.DispenserMenu; import net.minecraft.world.inventory.EnchantmentMenu; import net.minecraft.world.inventory.HopperMenu; -import net.minecraft.world.inventory.HorseInventoryMenu; import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.inventory.ItemCombinerMenu; import net.minecraft.world.inventory.MenuType; @@ -60,7 +60,7 @@ ItemCombinerMenu.class, BrewingStandMenu.class, BeaconMenu.class, - HorseInventoryMenu.class, + AbstractMountInventoryMenu.class, MerchantMenu.class, InventoryMenu.class, CraftingMenu.class diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_InventoryBridge_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_InventoryBridge_Inventory.java index 259d84bbe9d..b52e3a1c5c5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_InventoryBridge_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_InventoryBridge_Inventory.java @@ -29,8 +29,9 @@ import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.decoration.Mannequin; import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.vehicle.AbstractMinecartContainer; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecartContainer; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.MerchantContainer; import net.minecraft.world.inventory.ResultContainer; @@ -68,6 +69,7 @@ ResultContainer.class, AbstractMinecartContainer.class, ArmorStand.class, + Mannequin.class, Mob.class, MerchantContainer.class, CampfireBlockEntity.class, diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_ViewableBridge_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_ViewableBridge_Inventory.java index 69fa5771b1e..743832b4643 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_ViewableBridge_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/TraitMixin_ViewableBridge_Inventory.java @@ -26,9 +26,9 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.CompoundContainer; -import net.minecraft.world.entity.npc.AbstractVillager; import net.minecraft.world.entity.npc.ClientSideMerchant; -import net.minecraft.world.entity.vehicle.AbstractMinecartContainer; +import net.minecraft.world.entity.npc.villager.AbstractVillager; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecartContainer; import net.minecraft.world.inventory.PlayerEnderChestContainer; import net.minecraft.world.level.block.entity.BaseContainerBlockEntity; import net.minecraft.world.level.block.entity.LecternBlockEntity; diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/server/level/ServerPlayer_Mixin_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/server/level/ServerPlayer_Mixin_Inventory.java index 33d30bced91..1f74135c450 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/server/level/ServerPlayer_Mixin_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/server/level/ServerPlayer_Mixin_Inventory.java @@ -24,11 +24,11 @@ */ package org.spongepowered.common.mixin.inventory.impl.server.level; -import net.minecraft.core.NonNullList; -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundSetPlayerInventoryPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.ItemStack; +import org.spongepowered.api.item.inventory.Slot; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -38,6 +38,8 @@ import org.spongepowered.common.inventory.adapter.impl.slots.SlotAdapter; import org.spongepowered.common.item.util.ItemStackUtil; +import java.util.List; + @Mixin(targets = "net/minecraft/server/level/ServerPlayer$1") public abstract class ServerPlayer_Mixin_Inventory { @@ -46,14 +48,17 @@ public abstract class ServerPlayer_Mixin_Inventory { // @formatter:on @Inject(method = "sendInitialData", at = @At("RETURN")) - private void inventory$sendOffhand(final AbstractContainerMenu containerMenu, final NonNullList $$1, final ItemStack $$2, final int[] $$3, final CallbackInfo ci) { + private void inventory$sendOffhand( + final AbstractContainerMenu containerMenu, final List $$1, final ItemStack $$2, + final int[] $$3, final CallbackInfo ci + ) { if (containerMenu == this.this$0.inventoryMenu) { return; } - final org.spongepowered.api.item.inventory.Slot offhand + final Slot offhand = ((org.spongepowered.api.entity.living.player.server.ServerPlayer) this.this$0).inventory().offhand(); this.this$0.connection.send( - new ClientboundContainerSetSlotPacket(-2, 0, ((SlotAdapter) offhand).getOrdinal(), ItemStackUtil.toNative(offhand.peek()))); + new ClientboundSetPlayerInventoryPacket(((SlotAdapter) offhand).getOrdinal(), ItemStackUtil.toNative(offhand.peek()))); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/TraitMixin_TrackedMenuBridge_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/TraitMixin_TrackedMenuBridge_Inventory.java index 22771af4874..6a27e19b411 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/TraitMixin_TrackedMenuBridge_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/TraitMixin_TrackedMenuBridge_Inventory.java @@ -27,8 +27,8 @@ import net.minecraft.world.CompoundContainer; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.vehicle.AbstractMinecartContainer; -import net.minecraft.world.entity.vehicle.ChestBoat; +import net.minecraft.world.entity.vehicle.boat.ChestBoat; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecartContainer; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.MerchantContainer; import net.minecraft.world.inventory.ResultContainer; diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/TraitMixin_UniversalFabric_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/TraitMixin_UniversalFabric_Inventory.java index f39450f99bd..278b5f27edb 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/TraitMixin_UniversalFabric_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/TraitMixin_UniversalFabric_Inventory.java @@ -27,8 +27,8 @@ import net.minecraft.world.CompoundContainer; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.vehicle.AbstractMinecartContainer; -import net.minecraft.world.entity.vehicle.ChestBoat; +import net.minecraft.world.entity.vehicle.boat.AbstractChestBoat; +import net.minecraft.world.entity.vehicle.minecart.AbstractMinecartContainer; import net.minecraft.world.inventory.MerchantContainer; import net.minecraft.world.inventory.ResultContainer; import net.minecraft.world.inventory.TransientCraftingContainer; @@ -43,7 +43,7 @@ @Mixin(value = { AbstractMinecartContainer.class, - ChestBoat.class, + AbstractChestBoat.class, BaseContainerBlockEntity.class, CompoundContainer.class, TransientCraftingContainer.class, diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/entity/player/InventoryMixin_Bridge_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/entity/player/InventoryMixin_Bridge_Inventory.java index 76465fafa51..24ed5f49b31 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/entity/player/InventoryMixin_Bridge_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/entity/player/InventoryMixin_Bridge_Inventory.java @@ -24,69 +24,41 @@ */ package org.spongepowered.common.mixin.inventory.impl.world.entity.player; -import net.minecraft.core.NonNullList; -import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket; +import net.minecraft.network.protocol.game.ClientboundSetHeldSlotPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.common.bridge.world.entity.player.PlayerInventoryBridge; import org.spongepowered.common.inventory.adapter.InventoryAdapter; -import java.util.List; - @Mixin(Inventory.class) public abstract class InventoryMixin_Bridge_Inventory implements PlayerInventoryBridge, InventoryAdapter { - @Shadow public int selected; + @Shadow private int selected; @Shadow @Final public Player player; - @Shadow @Final public NonNullList items; - @Shadow @Final public NonNullList armor; - @Shadow @Final public NonNullList offhand; - @Shadow @Final private List> compartments; - @Shadow private int timesChanged; - - private int impl$lastTimesChanged = this.timesChanged; - - private int impl$offhandIndex; - - @Inject(method = "*", at = @At("RETURN"), remap = false) - private void onConstructed(final Player playerIn, final CallbackInfo ci) { - // Find offhand slot - for (final NonNullList inventory : this.compartments) { - if (inventory == this.offhand) { - break; - } - this.impl$offhandIndex += inventory.size(); - } - } + @Unique private int impl$lastTimesChanged = this.timesChanged; @Override public int bridge$getHeldItemIndex(final InteractionHand hand) { - switch (hand) { - case MAIN_HAND: - return this.selected; - case OFF_HAND: - return this.impl$offhandIndex; - default: - throw new AssertionError(hand); - } + return switch (hand) { + case MAIN_HAND -> this.selected; + case OFF_HAND -> Inventory.SLOT_OFFHAND; + default -> throw new AssertionError(hand); + }; } @Override public void bridge$setSelectedItem(int itemIndex, final boolean notify) { itemIndex = itemIndex % 9; - if (notify && this.player instanceof ServerPlayer) { - final ClientboundSetCarriedItemPacket packet = new ClientboundSetCarriedItemPacket(itemIndex); - ((ServerPlayer)this.player).connection.send(packet); + if (notify && this.player instanceof ServerPlayer sp) { + final var packet = new ClientboundSetHeldSlotPacket(itemIndex); + sp.connection.send(packet); } this.selected = itemIndex; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/AbstractContainerMenuMixin_Menu_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/AbstractContainerMenuMixin_Menu_Inventory.java index c6f78c4a7c3..058c2a34d5a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/AbstractContainerMenuMixin_Menu_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/AbstractContainerMenuMixin_Menu_Inventory.java @@ -28,7 +28,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.ClickAction; -import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; import org.checkerframework.checker.nullness.qual.Nullable; @@ -69,7 +69,7 @@ public abstract class AbstractContainerMenuMixin_Menu_Inventory implements MenuB // Called when clicking in an inventory // InventoryMenu Callback @Inject(method = "doClick", at = @At(value = "HEAD"), cancellable = true) - private void impl$onClick(final int slotId, final int dragType, final ClickType clickTypeIn, final Player player, final CallbackInfo ci) { + private void impl$onClick(final int slotId, final int dragType, final ContainerInput clickTypeIn, final Player player, final CallbackInfo ci) { final SpongeInventoryMenu menu = this.bridge$getMenu(); if (menu != null) { if (!menu.onClick(slotId, dragType, clickTypeIn, player, (org.spongepowered.api.item.inventory.Container) this)) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/Boat_TypeMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/AbstractCraftingMenuMixin_TrackedMenuBridge_Inventory.java similarity index 74% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/Boat_TypeMixin_API.java rename to src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/AbstractCraftingMenuMixin_TrackedMenuBridge_Inventory.java index a21cd6e2815..1d66bc49ace 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/entity/vehicle/Boat_TypeMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/AbstractCraftingMenuMixin_TrackedMenuBridge_Inventory.java @@ -22,25 +22,21 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.entity.vehicle; +package org.spongepowered.common.mixin.inventory.impl.world.inventory; -import net.minecraft.world.entity.vehicle.Boat; -import net.minecraft.world.level.block.Block; -import org.spongepowered.api.block.BlockType; -import org.spongepowered.api.data.type.BoatType; +import net.minecraft.world.inventory.AbstractCraftingMenu; +import net.minecraft.world.inventory.CraftingContainer; +import net.minecraft.world.inventory.ResultContainer; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -@Mixin(Boat.Type.class) -public abstract class Boat_TypeMixin_API implements BoatType { +@Mixin(AbstractCraftingMenu.class) +public abstract class AbstractCraftingMenuMixin_TrackedMenuBridge_Inventory { // @formatter:off - @Shadow @Final private Block planks; + @Shadow @Final protected CraftingContainer craftSlots; + @Shadow @Final protected ResultContainer resultSlots; // @formatter:on - @Override - public BlockType representedBlock() { - return (BlockType) this.planks; - } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/AbstractFurnaceMenuMixin_TrackedMenuBridge_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/AbstractFurnaceMenuMixin_TrackedMenuBridge_Inventory.java index 4d405bf7779..cccebc2a7e2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/AbstractFurnaceMenuMixin_TrackedMenuBridge_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/AbstractFurnaceMenuMixin_TrackedMenuBridge_Inventory.java @@ -24,6 +24,7 @@ */ package org.spongepowered.common.mixin.inventory.impl.world.inventory; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -31,7 +32,6 @@ import net.minecraft.world.inventory.ContainerData; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.RecipeBookType; -import net.minecraft.world.item.crafting.RecipeType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -41,14 +41,18 @@ @Mixin(AbstractFurnaceMenu.class) public abstract class AbstractFurnaceMenuMixin_TrackedMenuBridge_Inventory { - @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;Lnet/minecraft/world/item/crafting/RecipeType;Lnet/minecraft/world/inventory/RecipeBookType;ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/Container;Lnet/minecraft/world/inventory/ContainerData;)V", at = @At("RETURN")) - private void inventory$attachContainerMenu(final MenuType $$0, final RecipeType $$1, final RecipeBookType $$2, final int $$3, final Inventory $$4, - final Container $$5, final ContainerData $$6, final CallbackInfo ci) { - if ($$4 instanceof final TrackedMenuBridge trackedMenu) { + @Inject( + method = "(Lnet/minecraft/world/inventory/MenuType;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/inventory/RecipeBookType;ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/Container;Lnet/minecraft/world/inventory/ContainerData;)V", + at = @At("RETURN")) + private void inventory$attachContainerMenu( + final MenuType $$0, final ResourceKey $$1, + final RecipeBookType $$2, final int $$3, final Inventory inventory, final Container container, final ContainerData $$6, + final CallbackInfo ci) { + if (inventory instanceof final TrackedMenuBridge trackedMenu) { trackedMenu.bridge$trackContainerMenu((AbstractContainerMenu) (Object) this); } - if ($$5 instanceof final TrackedMenuBridge trackedMenu) { + if (container instanceof final TrackedMenuBridge trackedMenu) { trackedMenu.bridge$trackContainerMenu((AbstractContainerMenu) (Object) this); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/CraftingMenuMixin_TrackedMenuBridge_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/CraftingMenuMixin_TrackedMenuBridge_Inventory.java index 33dd9c1078e..1f06cc385b0 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/CraftingMenuMixin_TrackedMenuBridge_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/CraftingMenuMixin_TrackedMenuBridge_Inventory.java @@ -27,24 +27,15 @@ import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.ContainerLevelAccess; -import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.inventory.CraftingMenu; -import net.minecraft.world.inventory.ResultContainer; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.bridge.world.inventory.container.TrackedMenuBridge; @Mixin(CraftingMenu.class) -public abstract class CraftingMenuMixin_TrackedMenuBridge_Inventory { - - // @formatter:off - @Shadow @Final private CraftingContainer craftSlots; - @Shadow @Final private ResultContainer resultSlots; - // @formatter:on +public abstract class CraftingMenuMixin_TrackedMenuBridge_Inventory extends AbstractCraftingMenuMixin_TrackedMenuBridge_Inventory { @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/inventory/ContainerLevelAccess;)V", at = @At("RETURN")) private void inventory$attachContainerMenu(final int $$0, final Inventory $$1, final ContainerLevelAccess $$2, final CallbackInfo ci) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/HorseInventoryMenuMixin_TrackedMenuBridge_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/HorseInventoryMenuMixin_TrackedMenuBridge_Inventory.java index 4566094a93a..c82724ff7a6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/HorseInventoryMenuMixin_TrackedMenuBridge_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/HorseInventoryMenuMixin_TrackedMenuBridge_Inventory.java @@ -25,7 +25,7 @@ package org.spongepowered.common.mixin.inventory.impl.world.inventory; import net.minecraft.world.Container; -import net.minecraft.world.entity.animal.horse.AbstractHorse; +import net.minecraft.world.entity.animal.equine.AbstractHorse; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.HorseInventoryMenu; diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/InventoryMenuMixin_TrackedMenuBridge_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/InventoryMenuMixin_TrackedMenuBridge_Inventory.java index 624eb0e6007..088b78c8386 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/InventoryMenuMixin_TrackedMenuBridge_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/InventoryMenuMixin_TrackedMenuBridge_Inventory.java @@ -27,24 +27,15 @@ import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.inventory.InventoryMenu; -import net.minecraft.world.inventory.ResultContainer; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.bridge.world.inventory.container.TrackedMenuBridge; @Mixin(InventoryMenu.class) -public abstract class InventoryMenuMixin_TrackedMenuBridge_Inventory { - - // @formatter:off - @Shadow @Final private CraftingContainer craftSlots; - @Shadow @Final private ResultContainer resultSlots; - // @formatter:on +public abstract class InventoryMenuMixin_TrackedMenuBridge_Inventory extends AbstractCraftingMenuMixin_TrackedMenuBridge_Inventory { @Inject(method = "", at = @At("RETURN")) private void inventory$attachContainerMenu(final Inventory $$0, final boolean $$1, final Player $$2, final CallbackInfo ci) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/ItemCombinerMenuMixin_TrackedMenuBridge_Inventory.java b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/ItemCombinerMenuMixin_TrackedMenuBridge_Inventory.java index d5ac16172a7..d24d110bd99 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/ItemCombinerMenuMixin_TrackedMenuBridge_Inventory.java +++ b/src/mixins/java/org/spongepowered/common/mixin/inventory/impl/world/inventory/ItemCombinerMenuMixin_TrackedMenuBridge_Inventory.java @@ -29,9 +29,9 @@ import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.ContainerLevelAccess; import net.minecraft.world.inventory.ItemCombinerMenu; +import net.minecraft.world.inventory.ItemCombinerMenuSlotDefinition; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.ResultContainer; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -49,7 +49,8 @@ public abstract class ItemCombinerMenuMixin_TrackedMenuBridge_Inventory { // @formatter:on @Inject(method = "", at = @At("RETURN")) - private void inventory$attachContainerMenu(final @Nullable MenuType $$0, final int $$1, final Inventory $$2, final ContainerLevelAccess $$3, final CallbackInfo ci) { + private void inventory$attachContainerMenu( + MenuType $$0, int $$1, Inventory $$2, ContainerLevelAccess $$3, ItemCombinerMenuSlotDefinition $$4, CallbackInfo ci) { if ($$2 instanceof final TrackedMenuBridge trackedMenu) { trackedMenu.bridge$trackContainerMenu((AbstractContainerMenu) (Object) this); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/ipforward/network/protocol/game/ClientboundCommandsPacket_ArgumentNodeStub_IpForward.java b/src/mixins/java/org/spongepowered/common/mixin/ipforward/network/protocol/game/ClientboundCommandsPacket_ArgumentNodeStub_IpForward.java index bacf15322f2..eb926802337 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/ipforward/network/protocol/game/ClientboundCommandsPacket_ArgumentNodeStub_IpForward.java +++ b/src/mixins/java/org/spongepowered/common/mixin/ipforward/network/protocol/game/ClientboundCommandsPacket_ArgumentNodeStub_IpForward.java @@ -29,7 +29,7 @@ import net.minecraft.commands.synchronization.ArgumentTypeInfo; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -59,7 +59,7 @@ public final class ClientboundCommandsPacket_ArgumentNodeStub_IpForward { @Inject(method = "serializeCap(Lnet/minecraft/network/FriendlyByteBuf;Lnet/minecraft/commands/synchronization/ArgumentTypeInfo;Lnet/minecraft/commands/synchronization/ArgumentTypeInfo$Template;)V", at = @At("HEAD"), cancellable = true) private static , T extends ArgumentTypeInfo.Template> void ipForward$onSerializeCap( final FriendlyByteBuf buf, final ArgumentTypeInfo $$1, final ArgumentTypeInfo.Template $$2, final CallbackInfo ci) { - final @Nullable ResourceLocation key = BuiltInRegistries.COMMAND_ARGUMENT_TYPE.getKey($$1); + final @Nullable Identifier key = BuiltInRegistries.COMMAND_ARGUMENT_TYPE.getKey($$1); if (key == null || ClientboundCommandsPacket_ArgumentNodeStub_IpForward.ipForward$isVanillaArgument(key)) { return; } @@ -75,7 +75,7 @@ public final class ClientboundCommandsPacket_ArgumentNodeStub_IpForward { buf.writeBytes(commandData); } - private static boolean ipForward$isVanillaArgument(final ResourceLocation key) { + private static boolean ipForward$isVanillaArgument(final Identifier key) { return switch (key.getNamespace()) { case "brigadier" -> true; case "minecraft" -> switch (key.getPath()) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/ipforward/server/dedicated/DedicatedServerMixin_IpForward.java b/src/mixins/java/org/spongepowered/common/mixin/ipforward/server/dedicated/DedicatedServerMixin_IpForward.java index 6c39c5ff3b4..41a68e38868 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/ipforward/server/dedicated/DedicatedServerMixin_IpForward.java +++ b/src/mixins/java/org/spongepowered/common/mixin/ipforward/server/dedicated/DedicatedServerMixin_IpForward.java @@ -40,10 +40,12 @@ @Mixin(DedicatedServer.class) public class DedicatedServerMixin_IpForward { - @Shadow @Final private static Logger LOGGER; + @Shadow + @Final + private static Logger LOGGER; @Inject(method = "initServer", at = @At(value = "INVOKE_STRING", remap = false, target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;)V", - args = "ldc=**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!")) + args = "ldc=**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!")) private void ipForward$logEnabled(final CallbackInfoReturnable ci) { final IpForwardingCategory.Mode mode = SpongeConfigs.getCommon().get().ipForwarding.mode; if (mode != IpForwardingCategory.Mode.NONE) { @@ -52,10 +54,10 @@ public class DedicatedServerMixin_IpForward { } } - @WrapOperation(method = "initServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/GameProfileCache;setUsesAuthentication(Z)V")) - private void ipForwarding$proxyOnlineMode(final boolean value, final Operation original) { + @WrapOperation(method = "initServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/dedicated/DedicatedServer;setUsesAuthentication(Z)V")) + private void ipForwarding$proxyOnlineMode(final DedicatedServer instance, final boolean value, final Operation original) { final IpForwardingCategory ipForwarding = SpongeConfigs.getCommon().get().ipForwarding; - original.call(ipForwarding.mode != IpForwardingCategory.Mode.NONE + original.call(instance, ipForwarding.mode != IpForwardingCategory.Mode.NONE ? ipForwarding.onlineMode : value); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/ipforward/server/network/ServerLoginPacketListenerImplMixin_IpForward.java b/src/mixins/java/org/spongepowered/common/mixin/ipforward/server/network/ServerLoginPacketListenerImplMixin_IpForward.java index 2943887bfa1..a256c6a977d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/ipforward/server/network/ServerLoginPacketListenerImplMixin_IpForward.java +++ b/src/mixins/java/org/spongepowered/common/mixin/ipforward/server/network/ServerLoginPacketListenerImplMixin_IpForward.java @@ -25,8 +25,10 @@ package org.spongepowered.common.mixin.ipforward.server.network; +import com.google.common.collect.HashMultimap; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; import net.minecraft.network.Connection; import net.minecraft.network.protocol.login.ServerboundHelloPacket; import net.minecraft.server.MinecraftServer; @@ -86,15 +88,19 @@ public abstract class ServerLoginPacketListenerImplMixin_IpForward { if (((ConnectionBridge_IpForward) this.connection).bungeeBridge$getSpoofedUUID() != null) { uuid = ((ConnectionBridge_IpForward) this.connection).bungeeBridge$getSpoofedUUID(); } else { - uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + $$0.getName()).getBytes(StandardCharsets.UTF_8)); + uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + $$0.name()).getBytes(StandardCharsets.UTF_8)); } - $$0 = new GameProfile(uuid, $$0.getName()); + $$0 = new GameProfile(uuid, $$0.name()); if (((ConnectionBridge_IpForward) this.connection).bungeeBridge$getSpoofedProfile() != null) { + final var mutableProperties = HashMultimap.create($$0.properties()); + for (final Property property : ((ConnectionBridge_IpForward) this.connection).bungeeBridge$getSpoofedProfile()) { - $$0.getProperties().put(property.name(), property); + mutableProperties.put(property.name(), property); } + + $$0 = new GameProfile($$0.id(), $$0.name(), new PropertyMap(mutableProperties)); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/movementcheck/server/network/ServerGamePacketListenerImplMixin_MovementCheck.java b/src/mixins/java/org/spongepowered/common/mixin/movementcheck/server/network/ServerGamePacketListenerImplMixin_MovementCheck.java index 33baee1c932..1a2772c5ffa 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/movementcheck/server/network/ServerGamePacketListenerImplMixin_MovementCheck.java +++ b/src/mixins/java/org/spongepowered/common/mixin/movementcheck/server/network/ServerGamePacketListenerImplMixin_MovementCheck.java @@ -42,7 +42,7 @@ public abstract class ServerGamePacketListenerImplMixin_MovementCheck { @ModifyConstant(method = "handleMovePlayer", constant = @Constant(floatValue = 100.0F, ordinal = 0), slice = @Slice( from = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;isFallFlying()Z", ordinal = 0), - to = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;isSingleplayerOwner()Z", ordinal = 0) + to = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;getBoundingBox()Lnet/minecraft/world/phys/AABB;", ordinal = 0) ) ) private float movementCheck$onMovedTooQuickly(final float value) { @@ -55,7 +55,7 @@ public abstract class ServerGamePacketListenerImplMixin_MovementCheck { @ModifyConstant(method = "handleMovePlayer", constant = @Constant(doubleValue = 0.0625D, ordinal = 0), slice = @Slice( - from = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;isChangingDimension()Z", ordinal = 1), + from = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;isChangingDimension()Z", ordinal = 0), to = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;isSleeping()Z", ordinal = 1) ) ) @@ -70,7 +70,7 @@ public abstract class ServerGamePacketListenerImplMixin_MovementCheck { @ModifyConstant(method = "handleMoveVehicle", constant = @Constant(doubleValue = 0.0625D, ordinal = 0), slice = @Slice( from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;move(Lnet/minecraft/world/entity/MoverType;Lnet/minecraft/world/phys/Vec3;)V", ordinal = 0), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;absMoveTo(DDDFF)V") + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;absSnapTo(DDDFF)V") ) ) private double movementCheck$onVehicleMovedWronglyCheck(final double value) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/optimization/world/entity/TamableAnimalMixin_Optimization_Owner.java b/src/mixins/java/org/spongepowered/common/mixin/optimization/world/entity/TamableAnimalMixin_Optimization_Owner.java index d7394ce4112..ef76807c09b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/optimization/world/entity/TamableAnimalMixin_Optimization_Owner.java +++ b/src/mixins/java/org/spongepowered/common/mixin/optimization/world/entity/TamableAnimalMixin_Optimization_Owner.java @@ -25,22 +25,24 @@ package org.spongepowered.common.mixin.optimization.world.entity; import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.EntityReference; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.TamableAnimal; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.common.mixin.core.world.entity.AgableMobMixin; import java.util.Optional; -import java.util.UUID; @Mixin(TamableAnimal.class) public abstract class TamableAnimalMixin_Optimization_Owner extends AgableMobMixin { - @Shadow @Final protected static EntityDataAccessor> DATA_OWNERUUID_ID; - @Nullable private UUID cachedOwner$OwnerId; + @Shadow @Final protected static EntityDataAccessor>> DATA_OWNERUUID_ID; + @Nullable @Unique private EntityReference cachedOwner$OwnerId; /** * @author gabizou - July 26th, 2016 @@ -50,13 +52,26 @@ public abstract class TamableAnimalMixin_Optimization_Owner extends AgableMobMix */ @Nullable @Overwrite - public UUID getOwnerUUID() { + public EntityReference getOwnerReference() { if (this.cachedOwner$OwnerId == null) { this.cachedOwner$OwnerId = this.entityData.get(TamableAnimalMixin_Optimization_Owner.DATA_OWNERUUID_ID).orElse(null); } return this.cachedOwner$OwnerId; } + /** + * @author gabizou - January 12th, 2025 + * @reason stores the cached owner id to save constant lookups from the data watcher + * + * @param entity The entity to set as the owner + */ + @Overwrite + public void setOwner(final @javax.annotation.Nullable LivingEntity entity) { + final var reference = Optional.ofNullable(entity).map(EntityReference::of); + this.cachedOwner$OwnerId = reference.orElse(null); + this.entityData.set(DATA_OWNERUUID_ID, reference); + } + /** * @author gabizou - July 26th, 2016 * @reason stores the cached owner id @@ -64,7 +79,7 @@ public UUID getOwnerUUID() { * @param ownerUuid The owner id to set */ @Overwrite - public void setOwnerUUID(@Nullable final UUID ownerUuid) { + public void setOwnerReference(@Nullable final EntityReference ownerUuid) { this.cachedOwner$OwnerId = ownerUuid; this.entityData.set(TamableAnimalMixin_Optimization_Owner.DATA_OWNERUUID_ID, Optional.ofNullable(ownerUuid)); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/plugin/TestPlugin.java b/src/mixins/java/org/spongepowered/common/mixin/plugin/TestPlugin.java index a51d74ab859..56ce44006cd 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/plugin/TestPlugin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/plugin/TestPlugin.java @@ -24,11 +24,13 @@ */ package org.spongepowered.common.mixin.plugin; +import org.spongepowered.common.applaunch.test.TestEnvironment; + public final class TestPlugin extends AbstractMixinConfigPlugin { private final boolean active; public TestPlugin() { - this.active = "true".equals(System.getProperty("sponge.test.active")); + this.active = TestEnvironment.isActive(); } @Override diff --git a/src/mixins/java/org/spongepowered/common/mixin/plugin/entityactivation/EntityActivationRange.java b/src/mixins/java/org/spongepowered/common/mixin/plugin/entityactivation/EntityActivationRange.java index 6f123326cde..7d6585433cc 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/plugin/entityactivation/EntityActivationRange.java +++ b/src/mixins/java/org/spongepowered/common/mixin/plugin/entityactivation/EntityActivationRange.java @@ -25,7 +25,7 @@ package org.spongepowered.common.mixin.plugin.entityactivation; import com.google.common.collect.ImmutableMap; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.Mth; @@ -36,21 +36,21 @@ import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ambient.AmbientCreature; import net.minecraft.world.entity.animal.Animal; -import net.minecraft.world.entity.animal.Sheep; -import net.minecraft.world.entity.animal.WaterAnimal; -import net.minecraft.world.entity.boss.EnderDragonPart; +import net.minecraft.world.entity.animal.fish.WaterAnimal; +import net.minecraft.world.entity.animal.sheep.Sheep; import net.minecraft.world.entity.boss.enderdragon.EndCrystal; import net.minecraft.world.entity.boss.enderdragon.EnderDragon; +import net.minecraft.world.entity.boss.enderdragon.EnderDragonPart; import net.minecraft.world.entity.boss.wither.WitherBoss; -import net.minecraft.world.entity.decoration.Painting; +import net.minecraft.world.entity.decoration.painting.Painting; import net.minecraft.world.entity.item.FallingBlockEntity; import net.minecraft.world.entity.item.PrimedTnt; import net.minecraft.world.entity.monster.Enemy; -import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.villager.Villager; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.projectile.AbstractHurtingProjectile; import net.minecraft.world.entity.projectile.FireworkRocketEntity; import net.minecraft.world.entity.projectile.ThrowableProjectile; +import net.minecraft.world.entity.projectile.hurtingprojectile.AbstractHurtingProjectile; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.entity.EntitySection; import net.minecraft.world.level.entity.EntitySectionStorage; @@ -153,7 +153,7 @@ public static void initializeEntityActivationState(final Entity entity) { final EntityActivationRangeCategory config = configAdapter.get().entityActivationRange; final EntityTypeBridge type = (EntityTypeBridge) entity.getType(); - final ResourceLocation key = EntityType.getKey(entity.getType()); + final Identifier key = EntityType.getKey(entity.getType()); final byte activationType = spongeEntity.activation$getActivationType(); final String activationTypeName = EntityActivationRange.activationTypeMappings.getOrDefault(activationType, "misc"); if (!type.bridge$isActivationRangeInitialized()) { @@ -261,7 +261,7 @@ public static void activateEntities(final ServerLevel world) { private static void activateChunkEntities(final ServerPlayer player, final LevelChunk chunk) { final PersistentEntitySectionManager entityManager = ((ServerLevelAccessor) chunk.getLevel()).accessor$getEntityManager(); final EntitySectionStorage entitySectionStorage = ((PersistentEntitySectionManagerAccessor) entityManager).accessor$sectionStorage(); - entitySectionStorage.getExistingSectionsInChunk(chunk.getPos().toLong()).flatMap(EntitySection::getEntities).forEach(entity -> { + entitySectionStorage.getExistingSectionsInChunk(chunk.getPos().pack()).flatMap(EntitySection::getEntities).forEach(entity -> { if (!entity.chunkPosition().equals(chunk.getPos())) { return; } @@ -409,7 +409,7 @@ public static boolean checkIfActive(final Entity entity) { } public static void addEntityToConfig( - final boolean autoPopulate, final ResourceLocation key, final byte activationType, final String activationTypeName + final boolean autoPopulate, final Identifier key, final byte activationType, final String activationTypeName ) { final InheritableConfigHandle globalConfig = SpongeGameConfigs.getGlobalInheritable(); final EntityActivationRangeCategory activationConfig = globalConfig.get().entityActivationRange; diff --git a/src/mixins/java/org/spongepowered/common/mixin/test/server/MinecraftServerMixin_Test.java b/src/mixins/java/org/spongepowered/common/mixin/test/server/MinecraftServerMixin_Test.java index 79c6b77ab55..4109e7371c9 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/test/server/MinecraftServerMixin_Test.java +++ b/src/mixins/java/org/spongepowered/common/mixin/test/server/MinecraftServerMixin_Test.java @@ -54,7 +54,7 @@ public abstract class MinecraftServerMixin_Test extends ReentrantBlockableEventL // @formatter:on public MinecraftServerMixin_Test(final String name) { - super(name); + super(name, false); } @Inject(method = "", at = @At("RETURN")) diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/network/PacketProcessor_ListenerAndPacketMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/network/PacketProcessor_ListenerAndPacketMixin_Tracker.java new file mode 100644 index 00000000000..b6a90c85cae --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/network/PacketProcessor_ListenerAndPacketMixin_Tracker.java @@ -0,0 +1,43 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.tracker.network; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.network.PacketListener; +import net.minecraft.network.protocol.Packet; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.event.tracking.phase.packet.PacketPhaseUtil; + +@Mixin(targets = "net.minecraft.network.PacketProcessor$ListenerAndPacket") +public abstract class PacketProcessor_ListenerAndPacketMixin_Tracker { + + @WrapOperation(method = "handle", + at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/Packet;handle(Lnet/minecraft/network/PacketListener;)V")) + private void tracker$redirectProcessPacket(Packet packet, T listener, Operation original) { + PacketPhaseUtil.onProcessPacket(packet, listener, () -> original.call(packet, listener)); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/network/protocol/PacketUtilsMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/network/protocol/PacketUtilsMixin_Tracker.java deleted file mode 100644 index 5c35446f881..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/network/protocol/PacketUtilsMixin_Tracker.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.tracker.network.protocol; - -import net.minecraft.network.PacketListener; -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.PacketUtils; -import net.minecraft.util.thread.BlockableEventLoop; -import org.slf4j.Logger; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.event.tracking.phase.packet.PacketPhaseUtil; - -@Mixin(PacketUtils.class) -public abstract class PacketUtilsMixin_Tracker { - - // @formatter:off - @Shadow @Final private static Logger LOGGER; - // @formatter:on - - @Redirect(method = "ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/util/thread/BlockableEventLoop;)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/util/thread/BlockableEventLoop;executeIfPossible(Ljava/lang/Runnable;)V")) - private static void tracker$redirectProcessPacket(BlockableEventLoop threadTaskExecutor, Runnable runnable, - Packet packet, T packetListener, BlockableEventLoop blockableEventLoop) { - threadTaskExecutor.executeIfPossible(() -> { - if (packetListener.isAcceptingMessages()) { - PacketPhaseUtil.onProcessPacket(packet, packetListener); - } else { - LOGGER.debug("Ignoring packet due to disconnection: " + packet); - } - }); - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/MinecraftServerMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/MinecraftServerMixin_Tracker.java index 6c3b836a1c9..83b7c352133 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/MinecraftServerMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/MinecraftServerMixin_Tracker.java @@ -65,14 +65,14 @@ public abstract class MinecraftServerMixin_Tracker extends BlockableEventLoopMix private void tracker$wrapServerTick(final BooleanSupplier hasTimeLeft, final Operation original) { try ( final PhaseContext<@NonNull ?> context = TickPhase.Tick.SERVER_TICK - .createPhaseContext(PhaseTracker.SERVER) + .createPhaseContext(PhaseTracker.getServerInstanceExplicitly()) .server((MinecraftServer) (Object) this) ) { context.buildAndSwitch(); original.call(hasTimeLeft); } if (!((MinecraftServerBridge) this).bridge$insideTestEnvironment()) { - PhaseTracker.SERVER.ensureEmpty(); + PhaseTracker.getServerInstanceExplicitly().ensureEmpty(); } } @@ -86,7 +86,7 @@ public abstract class MinecraftServerMixin_Tracker extends BlockableEventLoopMix private void tracker$wrapWorldTick(final ServerLevel level, final BooleanSupplier hasTimeLeft, final Operation original) { try ( final PhaseContext<@NonNull ?> context = TickPhase.Tick.WORLD_TICK - .createPhaseContext(PhaseTracker.SERVER) + .createPhaseContext(PhaseTracker.getWorldInstance(level)) .world(level) ) { context.buildAndSwitch(); @@ -97,7 +97,7 @@ public abstract class MinecraftServerMixin_Tracker extends BlockableEventLoopMix @Inject(method = "wrapRunnable(Ljava/lang/Runnable;)Lnet/minecraft/server/TickTask;", at = @At("RETURN")) private void tracker$associatePhaseContextWithWrappedTask(final Runnable runnable, final CallbackInfoReturnable cir) { final TickTask returnValue = cir.getReturnValue(); - if (!PhaseTracker.SERVER.onSidedThread()) { + if (!PhaseTracker.getServerInstanceExplicitly().onSidedThread()) { final PhaseContext<@NonNull ?> phaseContext = PhaseTracker.getInstance().getPhaseContext(); if (phaseContext.isEmpty()) { return; @@ -108,7 +108,7 @@ public abstract class MinecraftServerMixin_Tracker extends BlockableEventLoopMix @WrapMethod(method = "doRunTask(Lnet/minecraft/server/TickTask;)V") private void tracker$wrapAndPerformContextSwitch(final TickTask task, final Operation original) { - try (final PhaseContext<@NonNull ?> context = PluginPhase.State.DELAYED_TASK.createPhaseContext(PhaseTracker.SERVER) + try (final PhaseContext<@NonNull ?> context = PluginPhase.State.DELAYED_TASK.createPhaseContext(PhaseTracker.getServerInstanceExplicitly()) .source(task) .setDelayedContextPopulator(((TickTaskBridge) task).bridge$getFrameModifier().orElse(null)) ) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/dedicated/DedicatedServerMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/dedicated/DedicatedServerMixin_Tracker.java index bc9d975b68a..e47a30607f6 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/dedicated/DedicatedServerMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/dedicated/DedicatedServerMixin_Tracker.java @@ -49,7 +49,7 @@ @Mixin(DedicatedServer.class) public abstract class DedicatedServerMixin_Tracker { - @Shadow public abstract int shadow$getSpawnProtectionRadius(); + @Shadow public abstract int shadow$spawnProtectionRadius(); /** * @author zml - March 9th, 2016 @@ -70,8 +70,8 @@ public boolean isUnderSpawnProtection(final ServerLevel worldIn, final BlockPos } } - final BlockPos spawnPoint = worldIn.getSharedSpawnPos(); - final int protectionRadius = this.shadow$getSpawnProtectionRadius(); + final BlockPos spawnPoint = worldIn.getRespawnData().pos(); + final int protectionRadius = this.shadow$spawnProtectionRadius(); return protectionRadius > 0 && Math.max(Math.abs(pos.getX() - spawnPoint.getX()), Math.abs(pos.getZ() - spawnPoint.getZ())) <= protectionRadius diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ChunkMapMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ChunkMapMixin_Tracker.java index c1ffcfa2fda..8560b7f254d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ChunkMapMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ChunkMapMixin_Tracker.java @@ -47,7 +47,7 @@ @Mixin(ChunkMap.class) public abstract class ChunkMapMixin_Tracker { - @Shadow @Final ServerLevel level; + @Shadow @Final private ServerLevel level; @Redirect(method = "addEntity(Lnet/minecraft/world/entity/Entity;)V", at = @At(value = "NEW", args = "class=java/lang/IllegalStateException", remap = false)) @@ -60,10 +60,11 @@ public abstract class ChunkMapMixin_Tracker { return exception; } - @Redirect(method = "lambda$prepareTickingChunk$27", + @Redirect(method = "lambda$prepareTickingChunk$2", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;startTickingChunk(Lnet/minecraft/world/level/chunk/LevelChunk;)V")) private void tracker$wrapUnpackTicks(final ServerLevel level, final LevelChunk chunk) { - if (!PhaseTracker.SERVER.onSidedThread()) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(level); + if (!phaseTracker.onSidedThread()) { new PrettyPrinter(60).add("Illegal Async Chunk Unpacking").centre().hr() .addWrapped("Someone is attempting to unpack chunk scheduled updates while off the main thread, this is" + "generally unsupported and Sponge would appreciate a report about this. Please attach " + @@ -76,10 +77,10 @@ public abstract class ChunkMapMixin_Tracker { .log(SpongeCommon.logger(), Level.ERROR); return; } - if (PhaseTracker.getInstance().getCurrentState() == GenerationPhase.State.CHUNK_LOADING) { + if (phaseTracker.getCurrentState() == GenerationPhase.State.CHUNK_LOADING) { return; } - try (final PhaseContext<@NonNull ?> ctx = GenerationPhase.State.CHUNK_LOADING.createPhaseContext(PhaseTracker.getInstance()) + try (final PhaseContext<@NonNull ?> ctx = GenerationPhase.State.CHUNK_LOADING.createPhaseContext(phaseTracker) .source(chunk) .world(level) .chunk(chunk)) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ChunkStatusTasksMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ChunkStatusTasksMixin_Tracker.java index dba2ef1d4fe..242a20fabc4 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ChunkStatusTasksMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ChunkStatusTasksMixin_Tracker.java @@ -47,7 +47,7 @@ @Mixin(ChunkStatusTasks.class) public abstract class ChunkStatusTasksMixin_Tracker { - @Inject(method = "lambda$full$2", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;runPostLoad()V"), slice = @Slice( + @Inject(method = "lambda$full$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;runPostLoad()V"), slice = @Slice( from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;setFullStatus(Ljava/util/function/Supplier;)V"), to = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;registerAllBlockEntitiesAfterLevelLoad()V") )) @@ -56,7 +56,8 @@ public abstract class ChunkStatusTasksMixin_Tracker { if (isFake) { return; } - if (!PhaseTracker.SERVER.onSidedThread()) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) chunk.getLevel()); + if (!phaseTracker.onSidedThread()) { new PrettyPrinter(60).add("Illegal Async Chunk Load").centre().hr() .addWrapped("Sponge relies on knowing when chunks are being loaded as chunks add entities" + " to the parented world for management. These operations are generally not" @@ -69,28 +70,29 @@ public abstract class ChunkStatusTasksMixin_Tracker { .log(SpongeCommon.logger(), Level.ERROR); return; } - if (PhaseTracker.getInstance().getCurrentState() == GenerationPhase.State.CHUNK_REGENERATING_LOAD_EXISTING) { + if (phaseTracker.getCurrentState() == GenerationPhase.State.CHUNK_REGENERATING_LOAD_EXISTING) { return; } - GenerationPhase.State.CHUNK_LOADING.createPhaseContext(PhaseTracker.getInstance()) + GenerationPhase.State.CHUNK_LOADING.createPhaseContext(phaseTracker) .source(chunk) .world((ServerLevel) chunk.getLevel()) .chunk(chunk) .buildAndSwitch(); } - @Inject(method = "lambda$full$2", + @Inject(method = "lambda$full$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;registerAllBlockEntitiesAfterLevelLoad()V", shift = At.Shift.BY, by = 2), slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;runPostLoad()V")) ) private static void tracker$endLoad(final ChunkAccess chunk, final WorldGenContext context, final GenerationChunkHolder holder, final CallbackInfoReturnable cir) { - if (!((LevelBridge) context.level()).bridge$isFake() && PhaseTracker.SERVER.onSidedThread()) { - if (PhaseTracker.getInstance().getCurrentState() == GenerationPhase.State.CHUNK_REGENERATING_LOAD_EXISTING) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(context.level()); + if (!((LevelBridge) context.level()).bridge$isFake() && phaseTracker.onSidedThread()) { + if (phaseTracker.getCurrentState() == GenerationPhase.State.CHUNK_REGENERATING_LOAD_EXISTING) { return; } // IF we're not on the main thread, - PhaseTracker.getInstance().getPhaseContext().close(); + phaseTracker.getPhaseContext().close(); } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerChunkCacheMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerChunkCacheMixin_Tracker.java index 7ab99989ace..956a50f2f1d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerChunkCacheMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerChunkCacheMixin_Tracker.java @@ -24,59 +24,54 @@ */ package org.spongepowered.common.mixin.tracker.server.level; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.MobCategory; import net.minecraft.world.level.NaturalSpawner; import net.minecraft.world.level.chunk.LevelChunk; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.event.tracking.phase.generation.GenerationPhase; +import java.util.List; + @Mixin(ServerChunkCache.class) public abstract class ServerChunkCacheMixin_Tracker { - @Redirect( - method = "tickChunks", + @WrapOperation( + method = "tickSpawningChunk", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/level/NaturalSpawner;spawnForChunk(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/chunk/LevelChunk;Lnet/minecraft/world/level/NaturalSpawner$SpawnState;ZZZ)V" + target = "Lnet/minecraft/world/level/NaturalSpawner;spawnForChunk(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/chunk/LevelChunk;Lnet/minecraft/world/level/NaturalSpawner$SpawnState;Ljava/util/List;)V" ) ) private void tracker$wrapEntitySpawner( - final ServerLevel serverWorld, - final LevelChunk targetChunk, - final NaturalSpawner.SpawnState manager, - final boolean something, - final boolean somethingElse, - final boolean somethingLast + final ServerLevel serverWorld, final LevelChunk targetChunk, final NaturalSpawner.SpawnState spawnState, + final List mobCategories, final Operation original ) { - try (final PhaseContext<@NonNull ?> context = GenerationPhase.State.WORLD_SPAWNER_SPAWNING.createPhaseContext(PhaseTracker.SERVER) + try (final PhaseContext<@NonNull ?> context = GenerationPhase.State.WORLD_SPAWNER_SPAWNING + .createPhaseContext(PhaseTracker.getWorldInstance(serverWorld)) .world(serverWorld)) { context.buildAndSwitch(); - NaturalSpawner.spawnForChunk(serverWorld, targetChunk, manager, something, somethingElse, somethingLast); + original.call(serverWorld, targetChunk, spawnState, mobCategories); } } - @Redirect( - method = "tickChunks", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/level/ServerLevel;tickCustomSpawners(ZZ)V" - ) - ) + @WrapOperation(method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;J)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;tickCustomSpawners(Z)V")) private void tracker$wrapGeneratorEntitySpawner( - final ServerLevel serverWorld, - final boolean spawnHostileMobs, - final boolean spawnPeacefulMobs + final ServerLevel serverWorld, final boolean spawnEnemies, final Operation wrapped ) { - try (final PhaseContext<@NonNull ?> context = GenerationPhase.State.WORLD_SPAWNER_SPAWNING.createPhaseContext(PhaseTracker.SERVER) + try (final PhaseContext<@NonNull ?> context = GenerationPhase.State.WORLD_SPAWNER_SPAWNING + .createPhaseContext(PhaseTracker.getWorldInstance(serverWorld)) .world(serverWorld)) { context.buildAndSwitch(); - serverWorld.tickCustomSpawners(spawnHostileMobs, spawnPeacefulMobs); + wrapped.call(serverWorld, spawnEnemies); } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerLevelMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerLevelMixin_Tracker.java index e7e5d636487..81c1d3b6e5c 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerLevelMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerLevelMixin_Tracker.java @@ -24,29 +24,47 @@ */ package org.spongepowered.common.mixin.tracker.server.level; +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.particles.ExplosionParticleInfo; +import net.minecraft.core.particles.ParticleOptions; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.PacketListener; +import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundExplodePacket; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.sounds.SoundEvent; import net.minecraft.util.RandomSource; +import net.minecraft.util.random.WeightedList; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockEventData; import net.minecraft.world.level.Explosion; +import net.minecraft.world.level.ExplosionDamageCalculator; import net.minecraft.world.level.Level; +import net.minecraft.world.level.ServerExplosion; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.piston.PistonBaseBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.Sponge; import org.spongepowered.api.block.entity.BlockEntity; +import org.spongepowered.api.entity.explosive.Explosive; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.SpongeEventFactory; @@ -54,21 +72,22 @@ import org.spongepowered.api.event.world.ExplosionEvent; import org.spongepowered.api.world.BlockChangeFlag; import org.spongepowered.api.world.LocatableBlock; -import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.util.PrettyPrinter; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.block.SpongeBlockSnapshot; import org.spongepowered.common.bridge.TrackableBridge; +import org.spongepowered.common.bridge.explosives.ExplosiveBridge; import org.spongepowered.common.bridge.server.level.ServerLevelBridge; import org.spongepowered.common.bridge.world.TrackedWorldBridge; +import org.spongepowered.common.bridge.world.entity.GrieferBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.bridge.world.level.TrackableBlockEventDataBridge; import org.spongepowered.common.bridge.world.level.block.state.BlockStateBridge; @@ -84,8 +103,10 @@ import org.spongepowered.common.event.tracking.context.transaction.GameTransaction; import org.spongepowered.common.event.tracking.context.transaction.block.ChangeBlock; import org.spongepowered.common.event.tracking.context.transaction.block.RemoveBlockEntity; -import org.spongepowered.common.event.tracking.context.transaction.effect.CheckBlockPostPlacementIsSameEffect; import org.spongepowered.common.event.tracking.context.transaction.effect.EffectResult; +import org.spongepowered.common.event.tracking.context.transaction.effect.InteractionAtArgs; +import org.spongepowered.common.event.tracking.context.transaction.effect.InteractionItemEffect; +import org.spongepowered.common.event.tracking.context.transaction.effect.InteractionUseItemOnBlockEffect; import org.spongepowered.common.event.tracking.context.transaction.effect.NotifyClientEffect; import org.spongepowered.common.event.tracking.context.transaction.effect.NotifyNeighborSideEffect; import org.spongepowered.common.event.tracking.context.transaction.effect.PerformBlockDropsFromDestruction; @@ -93,12 +114,18 @@ import org.spongepowered.common.event.tracking.context.transaction.effect.SetAndRegisterBlockEntityToLevelChunk; import org.spongepowered.common.event.tracking.context.transaction.effect.UpdateConnectingBlocksEffect; import org.spongepowered.common.event.tracking.context.transaction.effect.UpdateWorldRendererEffect; +import org.spongepowered.common.event.tracking.context.transaction.effect.UseItemArgs; +import org.spongepowered.common.event.tracking.context.transaction.effect.UseItemAtArgs; +import org.spongepowered.common.event.tracking.context.transaction.effect.UseItemAtEffect; +import org.spongepowered.common.event.tracking.context.transaction.effect.UseItemEffect; import org.spongepowered.common.event.tracking.context.transaction.effect.WorldBlockChangeCompleteEffect; import org.spongepowered.common.event.tracking.context.transaction.effect.WorldDestroyBlockLevelEffect; import org.spongepowered.common.event.tracking.context.transaction.pipeline.ChunkPipeline; +import org.spongepowered.common.event.tracking.context.transaction.pipeline.InteractionPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.PipelineCursor; import org.spongepowered.common.event.tracking.context.transaction.pipeline.TileEntityPipeline; import org.spongepowered.common.event.tracking.context.transaction.pipeline.WorldPipeline; +import org.spongepowered.common.event.tracking.phase.general.GeneralPhase; import org.spongepowered.common.event.tracking.phase.tick.TickPhase; import org.spongepowered.common.mixin.tracker.world.level.LevelMixin_Tracker; import org.spongepowered.common.util.Constants; @@ -111,24 +138,21 @@ import java.util.Optional; import java.util.UUID; import java.util.function.Consumer; -import java.util.function.Function; @Mixin(ServerLevel.class) public abstract class ServerLevelMixin_Tracker extends LevelMixin_Tracker implements TrackedWorldBridge { - // @formatting:off - @Shadow @Final List players; - // @formatting:on @WrapOperation( // This normally would target this.entityTickList.forEach((var2x) -> // but we don't have lambda syntax support yet. - method = "lambda$tick$2", + method = "lambda$tick$0", at = @At( value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;guardEntityTick(Ljava/util/function/Consumer;Lnet/minecraft/world/entity/Entity;)V") ) - private void tracker$wrapNormalEntityTick(final ServerLevel level, final Consumer entityUpdateConsumer, + private void tracker$wrapNormalEntityTick( + final ServerLevel level, final Consumer entityUpdateConsumer, final Entity entity, final Operation tick ) { TrackingUtil.tickEntity(entity, () -> tick.call(level, entityUpdateConsumer, entity)); @@ -141,9 +165,9 @@ public abstract class ServerLevelMixin_Tracker extends LevelMixin_Tracker implem * or we wrap in this method here. * * @param blockState The block state being ticked - * @param worldIn The world (this world) - * @param posIn The position of the block - * @param randomIn The world random + * @param worldIn The world (this world) + * @param posIn The position of the block + * @param randomIn The world random * @author gabizou - January 11th, 2020 - Minecraft 1.14.3 */ @WrapOperation(method = "tickBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/Block;)V", @@ -157,12 +181,12 @@ public abstract class ServerLevelMixin_Tracker extends LevelMixin_Tracker implem @WrapOperation(method = "tickFluid(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/material/Fluid;)V", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/material/FluidState;tick(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)V")) + target = "Lnet/minecraft/world/level/material/FluidState;tick(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)V")) private void tracker$wrapFluidTick( - final FluidState fluidState, final Level thisLevel, final BlockPos pos, + final FluidState fluidState, final ServerLevel level, final BlockPos pos, final BlockState blockState, final Operation tick ) { - TrackingUtil.updateTickFluid(this, fluidState, pos, () -> tick.call(fluidState, thisLevel, pos)); + TrackingUtil.updateTickFluid(this, fluidState, pos, blockState, () -> tick.call(fluidState, level, pos, blockState)); } /** @@ -184,42 +208,21 @@ public abstract class ServerLevelMixin_Tracker extends LevelMixin_Tracker implem @WrapOperation(method = "tickChunk(Lnet/minecraft/world/level/chunk/LevelChunk;I)V", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/material/FluidState;randomTick(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/util/RandomSource;)V" + target = "Lnet/minecraft/world/level/material/FluidState;randomTick(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;Lnet/minecraft/util/RandomSource;)V" ) ) private void tracker$wrapFluidRandomTick( - final FluidState fluidState, final Level worldIn, final BlockPos pos, + final FluidState fluidState, final ServerLevel worldIn, final BlockPos pos, final RandomSource random, final Operation tick) { TrackingUtil.randomTickFluid(this, fluidState, pos, this.random, () -> tick.call(fluidState, worldIn, pos, random)); } - @Inject( - method = "tickChunk", - at = @At( - value = "INVOKE_STRING", - target = "Lnet/minecraft/util/profiling/ProfilerFiller;push(Ljava/lang/String;)V", - args = "ldc=thunder" - ) - ) - private void tracker$startWeatherTickPhase(final LevelChunk param0, final int param1, final CallbackInfo ci) { - TickPhase.Tick.WEATHER.createPhaseContext(PhaseTracker.SERVER) - .buildAndSwitch(); - } - - @Inject( - method = "tickChunk", - at = @At( - value = "INVOKE_STRING", - target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", - args = "ldc=tickBlocks" - ) - ) - private void tracker$closeWeatherTickPhase(final LevelChunk param0, final int param1, final CallbackInfo ci) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); - if (context.getState() != TickPhase.Tick.WEATHER) { - throw new IllegalStateException("Expected to be in a Weather ticking state, but we aren't."); + @WrapMethod(method = "tickThunder") + private void tracker$startWeatherTickPhase(LevelChunk chunk, Operation original) { + try (final var ignored = TickPhase.Tick.WEATHER.createPhaseContext(PhaseTracker.getWorldInstance((ServerLevel) (Object) this)) + .buildAndSwitch()) { + original.call(chunk); } - context.close(); } @WrapOperation(method = "doBlockEvent(Lnet/minecraft/world/level/BlockEventData;)Z", @@ -227,7 +230,7 @@ public abstract class ServerLevelMixin_Tracker extends LevelMixin_Tracker implem private boolean tracker$wrapBlockStateEventReceived( final BlockState recievingState, final Level thisWorld, final BlockPos targetPos, final int eventId, final int flag, final Operation blockEvent, final BlockEventData data) { - return TrackingUtil.fireMinecraftBlockEvent(data, + return TrackingUtil.fireMinecraftBlockEvent((ServerLevel) (Object) this, data, () -> blockEvent.call(recievingState, thisWorld, targetPos, eventId, flag)); } @@ -243,7 +246,7 @@ public abstract class ServerLevelMixin_Tracker extends LevelMixin_Tracker implem ObjectLinkedOpenHashSet list, K curr, Operation original, final BlockPos pos, final Block blockIn, final int eventID, final int eventParam ) { - final PhaseContext<@NonNull ?> currentContext = PhaseTracker.getInstance().getPhaseContext(); + final PhaseContext<@NonNull ?> currentContext = PhaseTracker.getWorldInstance((ServerLevel) (Object) this).getPhaseContext(); final BlockEventData blockEventData = (BlockEventData) curr; final TrackableBlockEventDataBridge blockEvent = (TrackableBlockEventDataBridge) (Object) blockEventData; // Short circuit phase states who do not track during block events @@ -296,64 +299,116 @@ public abstract class ServerLevelMixin_Tracker extends LevelMixin_Tracker implem return original.call(list, curr); } - @SuppressWarnings({"unchecked", "rawtypes"}) - @Override - public Explosion tracker$triggerInternalExplosion(org.spongepowered.api.world.explosion.Explosion explosion, - final Function> contextCreator) { - // Sponge start - final Explosion originalExplosion = (Explosion) explosion; + @Redirect(method = "explode", at = @At(value = "NEW", + target = "(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;Lnet/minecraft/world/level/ExplosionDamageCalculator;Lnet/minecraft/world/phys/Vec3;FZLnet/minecraft/world/level/Explosion$BlockInteraction;)Lnet/minecraft/world/level/ServerExplosion;")) + private ServerExplosion tracker$onExplode(final ServerLevel $$0, final Entity entity, final DamageSource $$2, + final ExplosionDamageCalculator $$3, final Vec3 $$4, final float radius, final boolean $$6, + final Explosion.BlockInteraction blockInteraction) { + final var cannotGrief = entity instanceof GrieferBridge grieferBridge && !grieferBridge.bridge$canGrief(); + var finalRadius = radius; + if (entity instanceof ExplosiveBridge bridge) { + finalRadius = bridge.bridge$getExplosionRadius().orElse(finalRadius); + } + + return new ServerExplosion($$0, entity, $$2, $$3, $$4, finalRadius, $$6, cannotGrief ? Explosion.BlockInteraction.KEEP : blockInteraction); + } + + private org.spongepowered.api.world.explosion.Explosion tracker$apiExplosion; + + @WrapOperation(method = "explode", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/ServerExplosion;explode()I")) + private int tracker$onExplode(ServerExplosion instance, Operation original) { + var mcExplosion = instance; + final var entity = instance.getDirectSourceEntity(); + // TODO entity can be null, what is our API explosive then? + // TODO API explosive can be smth. that is not an entity? + final var thisWorld = (ServerWorld) this; + + final var explosionBuilder = org.spongepowered.api.world.explosion.Explosion.builder().from((org.spongepowered.api.world.explosion.Explosion) mcExplosion); + + if (!(entity instanceof final Explosive apiExplosive)) { + mcExplosion.explode(); + this.tracker$apiExplosion = explosionBuilder.build(); + return 0; + } + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) (Object) this); + final var detonateEvent = SpongeEventFactory.createDetonateExplosiveEvent(phaseTracker.currentCause(), + explosionBuilder, apiExplosive, (org.spongepowered.api.world.explosion.Explosion) instance); + if (Sponge.eventManager().post(detonateEvent)) { + this.tracker$cancelExplosionEffects(entity); + return 0; + } + + // Build the explosion from event + var apiExplosion = detonateEvent.explosionBuilder().build(); + if (apiExplosion.radius() <= 0) { + this.tracker$cancelExplosionEffects(entity); + return 0; + } + if (ShouldFire.EXPLOSION_EVENT_PRE) { // Set up the pre event - final ExplosionEvent.Pre - event = - SpongeEventFactory.createExplosionEventPre( - PhaseTracker.SERVER.currentCause(), - explosion, ((ServerWorld) this)); + final ExplosionEvent.Pre event = + SpongeEventFactory.createExplosionEventPre(phaseTracker.currentCause(), apiExplosion, thisWorld); if (SpongeCommon.post(event)) { - return (Explosion) explosion; + this.tracker$cancelExplosionEffects(entity); + return 0; } - explosion = event.explosion(); - } - final Explosion mcExplosion; - try { - // Since we already have the API created implementation Explosion, let's use it. - mcExplosion = (Explosion) explosion; - } catch (final Exception e) { - new PrettyPrinter(60).add("Explosion not compatible with this implementation").centre().hr() - .add("An explosion that was expected to be used for this implementation does not") - .add("originate from this implementation.") - .add(e) - .trace(); - return originalExplosion; - } - - try (final PhaseContext<@NonNull ?> ignored = contextCreator.apply(mcExplosion) - .source(((Optional) explosion.sourceExplosive()).orElse(this))) { - ignored.buildAndSwitch(); - final boolean damagesTerrain = explosion.shouldBreakBlocks(); - // Sponge End - - mcExplosion.explode(); - mcExplosion.finalizeExplosion(true); - if (!damagesTerrain) { - mcExplosion.clearToBlow(); + try { + // Since we already have the API created implementation Explosion, let's use it. + mcExplosion = (ServerExplosion) event.explosion(); + apiExplosion = event.explosion(); + } catch (final ClassCastException e) { + new PrettyPrinter(60).add("Explosion not compatible with this implementation").centre().hr() + .add("An explosion that was expected to be used for this implementation does not originate from this implementation.") + .trace(); + mcExplosion = (ServerExplosion) detonateEvent.explosionBuilder().build(); } + } - ServerLocation serverLocation = explosion.serverLocation(); - for (ServerPlayer serverPlayer : this.players) { - if (serverPlayer.distanceToSqr(serverLocation.x(), serverLocation.y(), serverLocation.z()) < 4096.0D) { - serverPlayer.connection - .send(new ClientboundExplodePacket(serverLocation.x(), serverLocation.y(), serverLocation.z(), - mcExplosion.radius(), mcExplosion.getToBlow(), - mcExplosion.getHitPlayers().get(serverPlayer), mcExplosion.getBlockInteraction(), - mcExplosion.getSmallExplosionParticles(), mcExplosion.getLargeExplosionParticles(), - mcExplosion.getExplosionSound())); - } - } + this.tracker$apiExplosion = apiExplosion; + try (final PhaseContext<@NonNull ?> ctx = GeneralPhase.State.EXPLOSION.createPhaseContext(phaseTracker).explosion(mcExplosion) + .source(((Optional) apiExplosion.sourceExplosive()).orElse(this))) { + ctx.buildAndSwitch(); + + return original.call(mcExplosion); + } + } + + @Inject(method = "explode", cancellable = true, at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/ServerExplosion;explode()I", shift = At.Shift.AFTER)) + private void tracker$onCancelled(final CallbackInfo ci) { + if (this.tracker$apiExplosion == null) { + ci.cancel(); + } + } + + /** + * See {@link net.minecraft.client.multiplayer.ClientPacketListener#handleExplosion} for client side handling + */ + @Redirect(method = "explode", at = @At(value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;send(Lnet/minecraft/network/protocol/Packet;)V")) + private void tracker$onClientboundExplodePacket(final ServerGamePacketListenerImpl instance, final Packet packet) { + final var originalPacket = (ClientboundExplodePacket) packet; + ((ServerLevelBridge) this).bridge$handleExplosionPacket(instance, this.tracker$apiExplosion, originalPacket); + } + + @Inject(method = "explode", at = @At(value = "RETURN")) + private void tracker$afterExplodeCleanup( + final Entity $$0, final DamageSource $$1, final ExplosionDamageCalculator $$2, final double $$3, + final double $$4, final double $$5, final float $$6, final boolean $$7, final Level.ExplosionInteraction $$8, + final ParticleOptions $$9, final ParticleOptions $$10, final WeightedList $$11, + final Holder $$12, final CallbackInfo ci) { + this.tracker$apiExplosion = null; + } + + @Unique + private void tracker$cancelExplosionEffects(final Entity entity) { + // TODO cancel effects + if (entity instanceof ExplosiveBridge explosiveBridge) { + explosiveBridge.bridge$cancelExplosion(); } - // Sponge End - return mcExplosion; } @Override @@ -367,8 +422,8 @@ public abstract class ServerLevelMixin_Tracker extends LevelMixin_Tracker implem if (this.bridge$isFake()) { return Optional.empty(); } - final PhaseTracker instance = PhaseTracker.getInstance(); - if (instance.getSidedThread() != PhaseTracker.SERVER.getSidedThread() && instance != PhaseTracker.SERVER) { + final PhaseTracker instance = PhaseTracker.getWorldInstance((ServerLevel) (Object) this); + if (!instance.onSidedThread()) { throw new UnsupportedOperationException("Cannot perform a tracked Block Change on a ServerWorld while not on the main thread!"); } final SpongeBlockChangeFlag spongeFlag = BlockChangeFlagManager.fromNativeInt(flags); @@ -383,6 +438,7 @@ public abstract class ServerLevelMixin_Tracker extends LevelMixin_Tracker implem return Optional.of(this.bridge$makePipeline(pos, currentState, newState, chunk, spongeFlag, Constants.World.DEFAULT_BLOCK_CHANGE_LIMIT)); } + @Unique private WorldPipeline.Builder bridge$makePipeline( final BlockPos pos, final BlockState currentState, @@ -396,13 +452,12 @@ public abstract class ServerLevelMixin_Tracker extends LevelMixin_Tracker implem // Then build and use the BlockPipeline final ChunkPipeline chunkPipeline = mixinChunk.bridge$createChunkPipeline(pos, newState, currentState, spongeFlag, limit); final WorldPipeline.Builder worldPipelineBuilder = WorldPipeline.builder(chunkPipeline); - worldPipelineBuilder.addEffect((pipeline, oldState, newState1, flag1, cursorLimit) -> { - if (oldState == null) { - return EffectResult.NULL_RETURN; - } - return EffectResult.NULL_PASS; - }) - .addEffect(CheckBlockPostPlacementIsSameEffect.getInstance()) + worldPipelineBuilder.addEffect((pipeline, oldState, args) -> { + if (oldState == null) { + return EffectResult.nullReturn(); + } + return EffectResult.nullPass(); + }) .addEffect(UpdateWorldRendererEffect.getInstance()) .addEffect(NotifyClientEffect.getInstance()) .addEffect(NotifyNeighborSideEffect.getInstance()) @@ -429,8 +484,8 @@ public boolean setBlock(final BlockPos pos, final net.minecraft.world.level.bloc if (this.bridge$isFake()) { return super.setBlock(pos, newState, flags, limit); } - final PhaseTracker instance = PhaseTracker.getInstance(); - if (instance.getSidedThread() != PhaseTracker.SERVER.getSidedThread() && instance != PhaseTracker.SERVER) { + final PhaseTracker instance = PhaseTracker.getWorldInstance((ServerLevel) (Object) this); + if (!instance.onSidedThread()) { throw new UnsupportedOperationException("Cannot perform a tracked Block Change on a ServerWorld while not on the main thread!"); } final SpongeBlockChangeFlag spongeFlag = BlockChangeFlagManager.fromNativeInt(flags); @@ -459,13 +514,10 @@ public boolean destroyBlock(final BlockPos pos, final boolean doDrops, @Nullable return false; } else { // Sponge Start - Sanity check against the PhaseTracker for instances - if (this.bridge$isFake()) { + final var instance = this.tracker$validateServerThread(); + if (instance == null) { return super.destroyBlock(pos, doDrops, p_241212_3_, limit); } - final PhaseTracker instance = PhaseTracker.getInstance(); - if (instance.getSidedThread() != PhaseTracker.SERVER.getSidedThread() && instance != PhaseTracker.SERVER) { - throw new UnsupportedOperationException("Cannot perform a tracked Block Change on a ServerWorld while not on the main thread!"); - } final FluidState fluidstate = this.shadow$getFluidState(pos); final BlockState emptyBlock = fluidstate.createLegacyBlock(); final SpongeBlockChangeFlag spongeFlag = BlockChangeFlagManager.fromNativeInt(3); @@ -483,9 +535,9 @@ public boolean destroyBlock(final BlockPos pos, final boolean doDrops, @Nullable } final WorldPipeline pipeline = pipelineBuilder - .addEffect((pipeline1, oldState, newState, flag, limit1) -> { - pipeline1.getServerWorld().gameEvent(GameEvent.BLOCK_DESTROY, oldState.pos, GameEvent.Context.of(p_241212_3_, oldState.state)); - return EffectResult.NULL_PASS; + .addEffect((pipeline1, oldState, args) -> { + pipeline1.getServerWorld().gameEvent(GameEvent.BLOCK_DESTROY, oldState.pos(), GameEvent.Context.of(p_241212_3_, oldState.state())); + return EffectResult.nullPass(); }) .addEffect(WorldBlockChangeCompleteEffect.getInstance()) .build(); @@ -496,7 +548,7 @@ public boolean destroyBlock(final BlockPos pos, final boolean doDrops, @Nullable @Override public SpongeBlockSnapshot bridge$createSnapshot(final net.minecraft.world.level.block.state.BlockState state, final BlockPos pos, - final BlockChangeFlag updateFlag + final BlockChangeFlag updateFlag ) { final SpongeBlockSnapshot.BuilderImpl builder = SpongeBlockSnapshot.BuilderImpl.pooled(); builder.reset(); @@ -531,6 +583,86 @@ public boolean destroyBlock(final BlockPos pos, final boolean doDrops, @Nullable return builder.build(); } + @Override + public InteractionPipeline<@NonNull InteractionAtArgs> bridge$startInteractionUseOnChange( + net.minecraft.world.level.Level worldIn, ServerPlayer playerIn, InteractionHand handIn, + BlockHitResult blockRaytraceResultIn, BlockState blockstate, ItemStack copiedStack + ) { + final var instance = this.tracker$validateServerThread(); + if (instance == null) { + return null; + } + final var args = new InteractionAtArgs(worldIn, playerIn, handIn, blockRaytraceResultIn, blockstate, copiedStack); + return new InteractionPipeline<>( + args, + InteractionResult.TRY_WITH_EMPTY_HAND, + InteractionUseItemOnBlockEffect.getInstance(), + instance.getPhaseContext().getTransactor() + ); + } + + + @Override + public InteractionPipeline<@NonNull InteractionAtArgs> bridge$startInteractionChange( + net.minecraft.world.level.Level worldIn, ServerPlayer playerIn, InteractionHand handIn, + BlockHitResult blockRaytraceResultIn, BlockState blockstate, ItemStack copiedStack + ) { + final var instance = this.tracker$validateServerThread(); + if (instance == null) { + return null; + } + final var args = new InteractionAtArgs(worldIn, playerIn, handIn, blockRaytraceResultIn, blockstate, copiedStack); + return new InteractionPipeline<>( + args, + InteractionResult.PASS, + InteractionItemEffect.getInstance(), + instance.getPhaseContext().getTransactor() + ); + } + + @Unique + private PhaseTracker tracker$validateServerThread() { + if (this.shadow$isDebug()) { // isClientSide is always false since this is WorldServer + return null; + } + if (this.bridge$isFake()) { + return null; + } + final var instance = PhaseTracker.getWorldInstance(this); + if (!instance.onSidedThread()) { + throw new UnsupportedOperationException("Cannot perform a tracked Block Change on a ServerWorld while not on the main thread!"); + } + return instance; + } + + @Override + public InteractionPipeline<@NonNull UseItemAtArgs> bridge$startItemInteractionChange( + net.minecraft.world.level.Level worldIn, ServerPlayer playerIn, InteractionHand handIn, + ItemStack copiedStack, BlockHitResult blockRaytraceResult, boolean creative + ) { + final var instance = this.tracker$validateServerThread(); + if (instance == null) { + return null; + } + final var args = new UseItemAtArgs(worldIn, playerIn,handIn, blockRaytraceResult, copiedStack, creative); + return new InteractionPipeline<>( + args, + InteractionResult.PASS, + UseItemAtEffect.getInstance(), + instance.getPhaseContext().getTransactor() + ); + } + + @Override + public InteractionPipeline bridge$startItemInteractionUseChange(net.minecraft.world.level.Level worldIn, ServerPlayer playerIn, InteractionHand handIn, ItemStack copiedStack) { + final var instance = this.tracker$validateServerThread(); + if (instance == null) { + return null; + } + final var args = new UseItemArgs(worldIn, playerIn, handIn, copiedStack, playerIn.gameMode); + return new InteractionPipeline<>(args, InteractionResult.PASS, UseItemEffect.getInstance(), instance.getPhaseContext().getTransactor()); + } + /** * Technically an overwrite, but because this is simply an override, we can * effectively do as we need to, which is determine if we are performing @@ -540,7 +672,7 @@ public boolean destroyBlock(final BlockPos pos, final boolean doDrops, @Nullable *

* After which, we may be able to appropriately associate the {@link net.minecraft.world.level.block.entity.BlockEntity} @@ -560,7 +692,8 @@ public boolean destroyBlock(final BlockPos pos, final boolean doDrops, @Nullable if (tileentity == null) { return; } - if (this.bridge$isFake() || PhaseTracker.SERVER.getSidedThread() != Thread.currentThread()) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) (Object) this); + if (this.bridge$isFake() || !phaseTracker.onSidedThread()) { // If we're fake or not on the server thread, well, we could effectively call // out whoever is trying to remove tile entities asynchronously.... super.shadow$removeBlockEntity(immutable); @@ -569,12 +702,12 @@ public boolean destroyBlock(final BlockPos pos, final boolean doDrops, @Nullable // Otherwise, let's go on and check if we're recording transactions, // and if so, log the tile entity removal (may associate with an existing transaction, // or create a new transaction. - final PhaseContext<@NonNull ?> current = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> current = phaseTracker.getPhaseContext(); if (current.getTransactor().logTileRemoval(tileentity, () -> (ServerLevel) (Object) this)) { final TileEntityPipeline pipeline = TileEntityPipeline.kickOff((ServerLevel) (Object) this, immutable) .addEffect(RemoveTileEntityFromChunkEffect.getInstance()) .build(); - pipeline.processEffects(current, new PipelineCursor(tileentity.getBlockState(), 0,immutable, tileentity, (Entity) null, Constants.World.DEFAULT_BLOCK_CHANGE_LIMIT)); + pipeline.processEffects(current, new PipelineCursor(tileentity.getBlockState(), immutable, tileentity, (Entity) null, Constants.World.DEFAULT_BLOCK_CHANGE_LIMIT)); return; } super.shadow$removeBlockEntity(immutable); @@ -584,7 +717,8 @@ public boolean destroyBlock(final BlockPos pos, final boolean doDrops, @Nullable @Override public void shadow$setBlockEntity(final net.minecraft.world.level.block.entity.BlockEntity proposed) { final BlockPos immutable = proposed.getBlockPos().immutable(); - if (this.bridge$isFake() || PhaseTracker.SERVER.getSidedThread() != Thread.currentThread()) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) (Object) this); + if (this.bridge$isFake() || !phaseTracker.onSidedThread()) { // If we're fake or not on the server thread, well, we could effectively call // out whoever is trying to remove tile entities asynchronously.... super.shadow$setBlockEntity(proposed); @@ -598,14 +732,14 @@ public boolean destroyBlock(final BlockPos pos, final boolean doDrops, @Nullable // Otherwise, let's go on and check if we're recording transactions, // and if so, log the tile entity removal (may associate with an existing transaction, // or create a new transaction. - final PhaseContext<@NonNull ?> current = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> current = phaseTracker.getPhaseContext(); if (current.doesBlockEventTracking()) { final net.minecraft.world.level.block.entity.@Nullable BlockEntity existing = this.shadow$getChunkAt(immutable).getBlockEntity(immutable); if (current.getTransactor().logTileReplacement(immutable, existing, proposed, () -> (ServerLevel) (Object) this)) { final TileEntityPipeline pipeline = TileEntityPipeline.kickOff((ServerLevel) (Object) this, immutable) .addEffect(SetAndRegisterBlockEntityToLevelChunk.getInstance()) .build(); - pipeline.processEffects(current, new PipelineCursor(proposed.getBlockState(), 0,immutable, proposed, (Entity) null, Constants.World.DEFAULT_BLOCK_CHANGE_LIMIT)); + pipeline.processEffects(current, new PipelineCursor(proposed.getBlockState(), immutable, proposed, (Entity) null, Constants.World.DEFAULT_BLOCK_CHANGE_LIMIT)); return; } } @@ -625,8 +759,8 @@ public boolean destroyBlock(final BlockPos pos, final boolean doDrops, @Nullable if (this.bridge$isFake()) { return; } - final PhaseTracker tracker = PhaseTracker.SERVER; - if (tracker.getSidedThread() != Thread.currentThread()) { + final PhaseTracker tracker = PhaseTracker.getWorldInstance((ServerLevel) (Object) this); + if (!tracker.onSidedThread()) { // TODO - async entity spawn logging return; } @@ -655,4 +789,25 @@ public boolean destroyBlock(final BlockPos pos, final boolean doDrops, @Nullable } } + /** + * Salvage for buggy-mod-async-world-adding-entities: some mods (e.g. Aether II's + * MoaNestFeature) call {@link ServerLevel#addFreshEntity(Entity)} directly from a + * worldgen worker thread instead of going through the {@link net.minecraft.world.level.WorldGenLevel} + * (concretely {@link net.minecraft.server.level.WorldGenRegion#addFreshEntity}) they were given. + * + *

The vanilla call chain ({@code ServerLevel#addFreshEntity} → + * {@code PersistentEntitySectionManager#addEntity}) is main-thread-only and races without + * Sponge in the picture. Rather than crash worldgen, capture the entity via + * {@link PhaseTracker#captureAsyncEntity(Entity)} so the per-tick drain task on the main + * thread can re-add it via the proper path. The boolean return is optimistic, mirroring how + * {@code WorldGenRegion#addFreshEntity} unconditionally returns {@code true}.

+ */ + @Inject(method = "addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z", at = @At("HEAD"), cancellable = true) + private void tracker$deferBuggyModAsyncWorldAddingEntities(final Entity entity, final CallbackInfoReturnable cir) { + if (!PhaseTracker.getWorldInstance((ServerLevel) (Object) this).onSidedThread()) { + PhaseTracker.captureAsyncEntity(entity); + cir.setReturnValue(true); + } + } + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerGameModeMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerGameModeMixin_Tracker.java index fa1a7aac74e..95d04b2ffc1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerGameModeMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerGameModeMixin_Tracker.java @@ -26,21 +26,22 @@ import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Cancellable; +import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; import com.llamalad7.mixinextras.sugar.ref.LocalRef; import net.minecraft.network.protocol.game.ClientboundSetHealthPacket; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; -import net.minecraft.world.ItemInteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.food.FoodData; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; @@ -57,17 +58,15 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.accessor.server.level.ServerPlayerAccessor; +import org.spongepowered.common.accessor.world.item.context.UseOnContextAccessor; import org.spongepowered.common.bridge.server.level.ServerPlayerGameModeBridge; +import org.spongepowered.common.bridge.world.TrackedWorldBridge; import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; -import org.spongepowered.common.event.tracking.context.transaction.ResultingTransactionBySideEffect; -import org.spongepowered.common.event.tracking.context.transaction.TransactionalCaptureSupplier; -import org.spongepowered.common.event.tracking.context.transaction.effect.InventoryEffect; -import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; import org.spongepowered.common.event.tracking.phase.player.PlayerPhase; import org.spongepowered.common.registry.provider.DirectionFacingProvider; import org.spongepowered.common.util.VecHelper; @@ -78,13 +77,12 @@ public abstract class ServerPlayerGameModeMixin_Tracker { @Shadow @Final protected ServerPlayer player; - @Shadow protected net.minecraft.server.level.ServerLevel level; - @Shadow public abstract boolean isCreative(); + @Shadow public abstract boolean shadow$isCreative(); @Inject(method = "useItem", cancellable = true, at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/item/ItemCooldowns;isOnCooldown(Lnet/minecraft/world/item/Item;)Z")) + target = "Lnet/minecraft/world/item/ItemCooldowns;isOnCooldown(Lnet/minecraft/world/item/ItemStack;)Z")) public void impl$callInteractItemSecondary(final ServerPlayer player, final Level level, final ItemStack stack, final InteractionHand hand, final CallbackInfoReturnable cir ) { @@ -98,13 +96,13 @@ public abstract class ServerPlayerGameModeMixin_Tracker { @WrapMethod(method = "useItemOn") private InteractionResult impl$wrapItemUse( final ServerPlayer player, final Level level, final ItemStack stack, final InteractionHand hand, final BlockHitResult blockHit, final Operation original, - @Share("useItem") final LocalRef useItemRef, @Share("useBlock") final LocalRef useBlockRef + @Share("useItem") final LocalRef useItemRef, @Share("useBlock") final LocalRef useBlockRef, @Share("context") final LocalRef> contextRef ) { final Vector3i blockPos = VecHelper.toVector3i(blockHit.getBlockPos()); final BlockSnapshot snapshot = ((ServerWorld) level).createSnapshot(blockPos); final Vector3d hitVec = VecHelper.toVector3d(blockHit.getLocation()); final org.spongepowered.api.util.Direction direction = DirectionFacingProvider.INSTANCE.getKey(blockHit.getDirection()).get(); - final InteractBlockEvent.Secondary event = SpongeCommonEventFactory.callInteractBlockEventSecondary(player, stack, hitVec, snapshot, direction, hand); + final InteractBlockEvent.Secondary.Pre event = SpongeCommonEventFactory.callInteractBlockEventSecondary(player, stack, hitVec, snapshot, direction, hand); ((ServerPlayerGameModeBridge) this).bridge$setInteractBlockRightClickCancelled(event.isCancelled()); if (event.isCancelled()) { @@ -115,16 +113,19 @@ public abstract class ServerPlayerGameModeMixin_Tracker { return InteractionResult.FAIL; } - useItemRef.set(event.useItemResult()); - useBlockRef.set(event.useBlockResult()); - - final PhaseTracker tracker = PhaseTracker.SERVER; + final PhaseTracker tracker = PhaseTracker.getWorldInstance((ServerLevel) level); try (final CauseStackManager.StackFrame frame = tracker.pushCauseFrame(); final PhaseContext context = PlayerPhase.State.PLAYER_INTERACT.createPhaseContext(tracker) .creator(player.getUUID()) .notifier(player.getUUID()) .containerLocation(ServerLocation.of((ServerWorld) level, blockPos))) { context.buildAndSwitch(); + context.getTransactor().logSecondaryInteractionTransaction(player, hitVec, snapshot, direction, event); + contextRef.set(context); + + useItemRef.set(event.useItemResult()); + useBlockRef.set(event.useBlockResult()); + frame.pushCause(event); frame.addContext(EventContextKeys.BLOCK_HIT, snapshot); @@ -132,45 +133,37 @@ public abstract class ServerPlayerGameModeMixin_Tracker { } } - @WrapOperation(method = "useItemOn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;useItemOn(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/phys/BlockHitResult;)Lnet/minecraft/world/ItemInteractionResult;")) - private ItemInteractionResult impl$onBlockInteraction( - final BlockState instance, final ItemStack stack, final Level level, final Player player, final InteractionHand hand, final BlockHitResult blockHit, final Operation original, - @Share("useBlock") final LocalRef useBlockRef, @Share("useBlockCancelled") final LocalBooleanRef useBlockCancelledRef - ) { + @Redirect(method = "useItemOn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;useItemOn(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/phys/BlockHitResult;)Lnet/minecraft/world/InteractionResult;")) + private InteractionResult impl$onBlockInteraction( + final BlockState block, final ItemStack stack, final Level level, final Player player, final InteractionHand hand, final BlockHitResult blockHit, + @Share("useBlock") final LocalRef useBlockRef, @Share("useBlockCancelled") final LocalBooleanRef useBlockCancelledRef, + @Share("context") final LocalRef> contextRef + ) { if (useBlockRef.get() == Tristate.FALSE) { useBlockCancelledRef.set(true); - return ItemInteractionResult.FAIL; + return InteractionResult.FAIL; } - final ItemInteractionResult result = original.call(instance, stack, level, player, hand, blockHit); - - // Log change in hand - final PhaseContext context = PhaseTracker.getInstance().getPhaseContext(); - final TransactionalCaptureSupplier transactor = context.getTransactor(); - if (!transactor.isEmpty()) { // TODO: API 14 composite event - try (final EffectTransactor ignored = context.getTransactor().pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance()))) { - transactor.logPlayerInventoryChange(this.player, PlayerInventoryTransaction.EventCreator.STANDARD); - this.player.inventoryMenu.broadcastChanges(); - } - } else { - transactor.logPlayerInventoryChange(this.player, PlayerInventoryTransaction.EventCreator.STANDARD); - try (final EffectTransactor ignored = context.getTransactor().pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance()))) { - this.player.inventoryMenu.broadcastChanges(); - } + final var pipeline = ((TrackedWorldBridge) level).bridge$startInteractionUseOnChange(level, (ServerPlayer) player, hand, blockHit, block, stack); + if (pipeline == null) { + return InteractionResult.FAIL; } - return result; + return pipeline.processInteraction(contextRef.get()); } - @WrapOperation(method = "useItemOn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;useWithoutItem(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/phys/BlockHitResult;)Lnet/minecraft/world/InteractionResult;")) + @Redirect(method = "useItemOn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;useWithoutItem(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/phys/BlockHitResult;)Lnet/minecraft/world/InteractionResult;")) private InteractionResult impl$onDefaultBlockInteraction( - final BlockState instance, final Level level, final Player player, final BlockHitResult blockHitResult, final Operation original, + final BlockState block, final Level level, final Player player, final BlockHitResult blockHit, + @Local(argsOnly = true) final InteractionHand hand, @Local(argsOnly = true) final ItemStack stack, + @Share("context") final LocalRef> contextRef, @Cancellable final CallbackInfoReturnable cir ) { final AbstractContainerMenu lastOpenContainer = player.containerMenu; final int containerCounter = ((ServerPlayerAccessor) player).accessor$containerCounter(); - final InteractionResult result = original.call(instance, level, player, blockHitResult); + final var pipeline = ((TrackedWorldBridge) level).bridge$startInteractionChange(level, (ServerPlayer) player, hand, blockHit, block, stack); + final InteractionResult result = pipeline.processInteraction(contextRef.get()); if (result.consumesAction() && lastOpenContainer == player.containerMenu && containerCounter != ((ServerPlayerAccessor) player).accessor$containerCounter()) { cir.setReturnValue(InteractionResult.FAIL); @@ -186,30 +179,26 @@ public abstract class ServerPlayerGameModeMixin_Tracker { } } - @Inject(method = "useItemOn", at = @At( + @Redirect(method = "useItemOn", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;hasInfiniteMaterials()Z")) + private boolean impl$removeCreativeBranch(final ServerPlayer self) { + return false; + } + + @Redirect(method = "useItemOn", at = @At( value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;useOn(Lnet/minecraft/world/item/context/UseOnContext;)Lnet/minecraft/world/InteractionResult;", - ordinal = 1, - shift = At.Shift.AFTER + ordinal = 1 )) - private void impl$afterItemInteractionNotCreative(final CallbackInfoReturnable cir) { - // Log change in hand - final PhaseContext context = PhaseTracker.getInstance().getPhaseContext(); - final TransactionalCaptureSupplier transactor = context.getTransactor(); - if (!transactor.isEmpty()) { // TODO: API 14 composite event - try (final EffectTransactor ignored = context.getTransactor().pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance()))) { - transactor.logPlayerInventoryChange(this.player, PlayerInventoryTransaction.EventCreator.STANDARD); - this.player.inventoryMenu.broadcastChanges(); - } - } else { - transactor.logPlayerInventoryChange(this.player, PlayerInventoryTransaction.EventCreator.STANDARD); - try (final EffectTransactor ignored = context.getTransactor().pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance()))) { - this.player.inventoryMenu.broadcastChanges(); - } - } + private InteractionResult impl$onItemInteraction( + final ItemStack stack, final UseOnContext useOnContext, + @Share("context") final LocalRef> contextRef + ) { + final Level level = useOnContext.getLevel(); + final var pipeline = ((TrackedWorldBridge) level).bridge$startItemInteractionChange(level, (ServerPlayer) useOnContext.getPlayer(), useOnContext.getHand(), stack, ((UseOnContextAccessor) useOnContext).accessor$hitResult(), this.shadow$isCreative()); + return pipeline.processInteraction(contextRef.get()); } - @Inject(method = "useItemOn", at = @At(value = "FIELD", target = "Lnet/minecraft/world/InteractionResult;PASS:Lnet/minecraft/world/InteractionResult;", ordinal = 1)) + @Inject(method = "useItemOn", at = @At(value = "FIELD", target = "Lnet/minecraft/world/InteractionResult;PASS:Lnet/minecraft/world/InteractionResult$Pass;", ordinal = 1)) private void impl$endWithNoInteraction(final CallbackInfoReturnable cir, @Share("useBlockCancelled") final LocalBooleanRef useBlockCancelledRef) { if (useBlockCancelledRef.get()) { ((ServerPlayerGameModeBridge) this).bridge$setInteractBlockRightClickCancelled(true); diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerMixin_Tracker.java index 99dfc9c0bad..7b3ac74fb0d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerMixin_Tracker.java @@ -25,15 +25,14 @@ package org.spongepowered.common.mixin.tracker.server.level; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.ItemStack; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.item.inventory.ItemStackSnapshot; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.common.bridge.world.entity.PlatformEntityBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.event.SpongeCommonEventFactory; @@ -47,33 +46,20 @@ @Mixin(ServerPlayer.class) public abstract class ServerPlayerMixin_Tracker extends PlayerMixin_Tracker { - /** - * @author gabizou - June 4th, 2016 - * @author i509VCB - February 17th, 2020 - 1.14.4 - * @author gabizou - December 31st, 2021 - 1.16.5 - * @reason We inject a construct event for the item drop and conveniently - * can redirect the super call. - */ - @WrapOperation( - method = "drop(Lnet/minecraft/world/item/ItemStack;ZZ)Lnet/minecraft/world/entity/item/ItemEntity;", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/entity/player/Player;drop(Lnet/minecraft/world/item/ItemStack;ZZ)Lnet/minecraft/world/entity/item/ItemEntity;" - ) - ) - @Nullable - private ItemEntity tracker$throwItemDrop( - final ServerPlayer instance, final ItemStack droppedItem, final boolean dropAround, + + @Override + protected @Nullable ItemEntity tracker$throwItemDrop( + final LivingEntity instance, final ItemStack droppedItem, final boolean dropAround, final boolean traceItem, final Operation wrapped ) { if (droppedItem.isEmpty()) { return null; } if (((PlatformEntityBridge) this).bridge$isFakePlayer()) { - return super.shadow$drop(droppedItem, dropAround, traceItem); + return wrapped.call(instance, droppedItem, dropAround, traceItem); } if (((LevelBridge) this.shadow$level()).bridge$isFake()) { - return super.shadow$drop(droppedItem, dropAround, traceItem); + return wrapped.call(instance, droppedItem, dropAround, traceItem); } final double posX1 = this.shadow$getX(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/network/ServerGamePacketListenerImplMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/network/ServerGamePacketListenerImplMixin_Tracker.java index 5a3e28c562c..a4d511670ec 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/network/ServerGamePacketListenerImplMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/network/ServerGamePacketListenerImplMixin_Tracker.java @@ -24,6 +24,8 @@ */ package org.spongepowered.common.mixin.tracker.server.network; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; @@ -54,17 +56,17 @@ public abstract class ServerGamePacketListenerImplMixin_Tracker { @Shadow public ServerPlayer player; - @Redirect(method = "tick", + @WrapOperation(method = "tickPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;doTick()V")) - private void tracker$wrapPlayerTickWithPhase(final ServerPlayer player) { + private void tracker$wrapPlayerTickWithPhase(ServerPlayer instance, Operation original) { if (((PlatformEntityBridge) player).bridge$isFakePlayer() || ((LevelBridge) player.level()).bridge$isFake()) { - player.doTick(); + original.call(instance); return; } - try (final PlayerTickContext context = TickPhase.Tick.PLAYER.createPhaseContext(PhaseTracker.SERVER).source(player)) { + try (final PlayerTickContext context = TickPhase.Tick.PLAYER.createPhaseContext(PhaseTracker.getWorldInstance(this.player.level())).source(player)) { context.buildAndSwitch(); PhaseTracker.LOGGER.trace(TrackingUtil.PLAYER_TICK, () -> "Wrapping Player Tick: " + player.toString()); - player.doTick(); + original.call(instance); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/players/PlayerListMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/players/PlayerListMixin_Tracker.java index a552b3ff9de..72fc7596a76 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/players/PlayerListMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/players/PlayerListMixin_Tracker.java @@ -47,7 +47,7 @@ public class PlayerListMixin_Tracker { final Entity.RemovalReason reason ) { try (final GeneralizedContext context = PlayerPhase.State.PLAYER_LOGOUT - .createPhaseContext(PhaseTracker.SERVER) + .createPhaseContext(PhaseTracker.getWorldInstance(world)) .source(player)) { context.buildAndSwitch(); world.removePlayerImmediately(player, reason); @@ -57,7 +57,7 @@ public class PlayerListMixin_Tracker { @Redirect(method = "placeNewPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;initInventoryMenu()V")) private void tracker$onPlaceNewPlayerInitMenu(final ServerPlayer player) { - try (final PhaseContext context = BlockPhase.State.RESTORING_BLOCKS.createPhaseContext(PhaseTracker.SERVER).source(player);) { + try (final PhaseContext context = BlockPhase.State.RESTORING_BLOCKS.createPhaseContext(PhaseTracker.getWorldInstance(player.level())).source(player);) { context.buildAndSwitch(); player.initInventoryMenu(); } @@ -66,7 +66,7 @@ public class PlayerListMixin_Tracker { @Redirect(method = "respawn", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;initInventoryMenu()V")) private void tracker$onRespawnInitMenu(final ServerPlayer player) { - try (final PhaseContext context = BlockPhase.State.RESTORING_BLOCKS.createPhaseContext(PhaseTracker.SERVER).source(player);) { + try (final PhaseContext context = BlockPhase.State.RESTORING_BLOCKS.createPhaseContext(PhaseTracker.getWorldInstance(player.level())).source(player);) { context.buildAndSwitch(); player.initInventoryMenu(); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/util/thread/BlockableEventLoopMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/util/thread/BlockableEventLoopMixin_Tracker.java index 57f6d33a19d..4e9de405fd7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/util/thread/BlockableEventLoopMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/util/thread/BlockableEventLoopMixin_Tracker.java @@ -24,14 +24,16 @@ */ package org.spongepowered.common.mixin.tracker.util.thread; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.util.thread.BlockableEventLoop; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.event.tracking.phase.plugin.BasicPluginContext; import org.spongepowered.common.event.tracking.phase.plugin.PluginPhase; +import org.spongepowered.common.launch.Launch; @Mixin(BlockableEventLoop.class) public abstract class BlockableEventLoopMixin_Tracker { @@ -40,12 +42,12 @@ public abstract class BlockableEventLoopMixin_Tracker { @Shadow protected abstract void dropAllTasks(); // @formatter:on - @Redirect(method = "execute", + @WrapOperation(method = "doRunTask", at = @At( value = "INVOKE", target = "Ljava/lang/Runnable;run()V", remap = false)) - private void tracker$callOnMainThreadWithPhaseState(final Runnable runnable) { + private void tracker$callOnMainThreadWithPhaseState(Runnable instance, Operation original) { // The server thread has been killed, we are unable to process requests any further. if (this.tracker$isServerAndIsServerStopped()) { this.dropAllTasks(); @@ -53,9 +55,10 @@ public abstract class BlockableEventLoopMixin_Tracker { } try (final BasicPluginContext context = PluginPhase.State.SCHEDULED_TASK.createPhaseContext(PhaseTracker.getInstance()) - .source(runnable)) { + .source(instance) + .container(Launch.instance().minecraftPlugin())) { context.buildAndSwitch(); - runnable.run(); + original.call(instance); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/EntityMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/EntityMixin_Tracker.java index 95b6427e0f2..5b0234ffea4 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/EntityMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/EntityMixin_Tracker.java @@ -26,6 +26,7 @@ import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.Component; +import net.minecraft.server.level.ServerLevel; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; @@ -88,16 +89,16 @@ public abstract class EntityMixin_Tracker implements DelegatingConfigTrackableBr protected @MonotonicNonNull EffectTransactor tracker$dropsTransactor = null; @Inject( - method = "spawnAtLocation(Lnet/minecraft/world/item/ItemStack;F)Lnet/minecraft/world/entity/item/ItemEntity;", + method = "spawnAtLocation(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/item/ItemStack;F)Lnet/minecraft/world/entity/item/ItemEntity;", at = @At("HEAD") ) - private void tracker$logEntityDropTransactionIfNecessary(final ItemStack stack, final float offsetY, - final CallbackInfoReturnable cir) { - final PhaseTracker instance = PhaseTracker.SERVER; - if (!instance.onSidedThread()) { + private void tracker$logEntityDropTransactionIfNecessary(final ServerLevel level, final ItemStack stack, final float offsetY, + final CallbackInfoReturnable cir) { + if (((LevelBridge) level).bridge$isFake()) { return; } - if (((LevelBridge) this.shadow$level()).bridge$isFake()) { + final PhaseTracker instance = PhaseTracker.getWorldInstance(level); + if (!instance.onSidedThread()) { return; } final PhaseContext<@NonNull ?> context = instance.getPhaseContext(); @@ -111,11 +112,11 @@ public abstract class EntityMixin_Tracker implements DelegatingConfigTrackableBr @Inject(method = "setRemoved(Lnet/minecraft/world/entity/Entity$RemovalReason;)V", at = @At("RETURN")) private void tracker$ensureDropEffectCompleted(final Entity.RemovalReason reason, final CallbackInfo ci) { - final PhaseTracker instance = PhaseTracker.SERVER; - if (!instance.onSidedThread()) { + if (((LevelBridge) this.shadow$level()).bridge$isFake()) { return; } - if (((LevelBridge) this.shadow$level()).bridge$isFake()) { + final PhaseTracker instance = PhaseTracker.getWorldInstance((ServerLevel) this.shadow$level()); + if (!instance.onSidedThread()) { return; } final PhaseContext<@NonNull ?> context = instance.getPhaseContext(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/EntityTypeMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/EntityTypeMixin_Tracker.java index 1c1a0fbc372..314ccd6ac74 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/EntityTypeMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/EntityTypeMixin_Tracker.java @@ -28,8 +28,6 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.entity.EntityType; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.bridge.RegistryBackedTrackableBridge; import org.spongepowered.common.config.SpongeGameConfigs; import org.spongepowered.common.config.tracker.TrackerCategory; @@ -37,21 +35,6 @@ @Mixin(EntityType.class) public abstract class EntityTypeMixin_Tracker implements RegistryBackedTrackableBridge> { - @Redirect(method = "register", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/core/Registry;register(Lnet/minecraft/core/Registry;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;" - ) - ) - private static Object impl$initializeTrackerState(final Registry registry, final String key, final Object toRegister) { - final Object registered = Registry.register(registry, key, toRegister); - - final RegistryBackedTrackableBridge> trackableBridge = (RegistryBackedTrackableBridge>) toRegister; - trackableBridge.bridge$refreshTrackerStates(); - - return registered; - } - @Override public TrackerCategory bridge$trackerCategory() { return SpongeGameConfigs.getTracker().get().entity; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/GameRuleCommandMixin.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/EntityTypesMixin_Tracker.java similarity index 54% rename from src/mixins/java/org/spongepowered/common/mixin/core/server/commands/GameRuleCommandMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/EntityTypesMixin_Tracker.java index c841d31eee9..ea99675dd9e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/commands/GameRuleCommandMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/EntityTypesMixin_Tracker.java @@ -22,27 +22,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.server.commands; +package org.spongepowered.common.mixin.tracker.world.entity; -import com.mojang.brigadier.context.CommandContext; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.commands.GameRuleCommand; -import net.minecraft.world.level.GameRules; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EntityTypes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.bridge.RegistryBackedTrackableBridge; -@Mixin(GameRuleCommand.class) -public abstract class GameRuleCommandMixin { +@Mixin(EntityTypes.class) +public abstract class EntityTypesMixin_Tracker { - @Redirect(method = "setRule", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getGameRules()Lnet/minecraft/world/level/GameRules;")) - private static GameRules impl$usePerWorldGameRules(final MinecraftServer server, final CommandContext context) { - return context.getSource().getLevel().getLevelData().getGameRules(); - } + @Redirect(method = "register(Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/entity/EntityType$Builder;)Lnet/minecraft/world/entity/EntityType;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/core/Registry;register(Lnet/minecraft/core/Registry;Lnet/minecraft/resources/ResourceKey;Ljava/lang/Object;)Ljava/lang/Object;" + ) + ) + private static V impl$initializeTrackerState(final Registry registry, final ResourceKey key, final V toRegister) { + final V registered = Registry.register(registry, key, toRegister); + + final RegistryBackedTrackableBridge> trackableBridge = (RegistryBackedTrackableBridge>) toRegister; + trackableBridge.bridge$refreshTrackerStates(); - @Redirect(method = "queryRule", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getGameRules()Lnet/minecraft/world/level/GameRules;")) - private static GameRules impl$usePerWorldGameRules(final MinecraftServer server, final CommandSourceStack source) { - return source.getLevel().getLevelData().getGameRules(); + return registered; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/LivingEntityMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/LivingEntityMixin_Tracker.java index 6f7e33dd68a..4531442bff5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/LivingEntityMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/LivingEntityMixin_Tracker.java @@ -24,12 +24,18 @@ */ package org.spongepowered.common.mixin.tracker.world.entity; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.CombatEntry; import net.minecraft.world.damagesource.CombatTracker; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemStack; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.asm.mixin.Mixin; @@ -62,6 +68,29 @@ public abstract class LivingEntityMixin_Tracker extends EntityMixin_Tracker { // @formatter:on + /** + * @author gabizou - June 4th, 2016 + * @author i509VCB - February 17th, 2020 - 1.14.4 + * @author gabizou - December 31st, 2021 - 1.16.5 + * @author gabizou - February 8th, 2025 - 25w06a + * + * @reason We wrap the original call to allow for throwing events for the server player instance + */ + @WrapOperation( + method = "drop(Lnet/minecraft/world/item/ItemStack;ZZ)Lnet/minecraft/world/entity/item/ItemEntity;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;createItemStackToDrop(Lnet/minecraft/world/item/ItemStack;ZZ)Lnet/minecraft/world/entity/item/ItemEntity;" + ) + ) + @Nullable + protected ItemEntity tracker$throwItemDrop( + final LivingEntity instance, final ItemStack droppedItem, final boolean dropAround, + final boolean traceItem, final Operation wrapped + ) { + return wrapped.call(instance, droppedItem, dropAround, traceItem); + } + @Override protected void tracker$populateDeathContextIfNeeded( final CauseStackManager.StackFrame frame, final EntityTickContext context @@ -96,12 +125,12 @@ public abstract class LivingEntityMixin_Tracker extends EntityMixin_Tracker { at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;tickDeath()V")) private void tracker$enterDeathPhase(final LivingEntity livingEntity) { - final PhaseTracker instance = PhaseTracker.SERVER; - if (!instance.onSidedThread()) { + if (((LevelBridge) this.shadow$level()).bridge$isFake()) { this.shadow$tickDeath(); return; } - if (((LevelBridge) this.shadow$level()).bridge$isFake()) { + final PhaseTracker instance = PhaseTracker.getWorldInstance((ServerLevel) livingEntity.level()); + if (!instance.onSidedThread()) { this.shadow$tickDeath(); return; } @@ -120,18 +149,18 @@ public abstract class LivingEntityMixin_Tracker extends EntityMixin_Tracker { * @reason Instead of inlining the onDeath method with the main mixin, we can "toggle" * the usage of the death state control in the tracker mixin. */ - @Redirect(method = "hurt", + @Redirect(method = "hurtServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;die(Lnet/minecraft/world/damagesource/DamageSource;)V" ) ) private void tracker$wrapOnDeathWithState(final LivingEntity thisEntity, final DamageSource cause) { // Sponge Start - notify the cause tracker - final PhaseTracker instance = PhaseTracker.SERVER; - if (!instance.onSidedThread()) { + if (((LevelBridge) this.shadow$level()).bridge$isFake()) { return; } - if (((LevelBridge) this.shadow$level()).bridge$isFake()) { + final PhaseTracker instance = PhaseTracker.getWorldInstance((ServerLevel) thisEntity.level()); + if (!instance.onSidedThread()) { return; } final PhaseContext<@NonNull ?> context = instance.getPhaseContext(); @@ -151,12 +180,12 @@ public abstract class LivingEntityMixin_Tracker extends EntityMixin_Tracker { at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;pushEntities()V") ) private void tracker$switchIntoCollisions(final LivingEntity livingEntity) { - if (this.shadow$level().isClientSide) { + if (this.shadow$level().isClientSide()) { this.shadow$pushEntities(); return; } try (final PhaseContext<@NonNull ?> context = EntityPhase.State.COLLISION - .createPhaseContext(PhaseTracker.SERVER) + .createPhaseContext(PhaseTracker.getWorldInstance((ServerLevel) livingEntity.level())) .source(livingEntity) ) { context.buildAndSwitch(); @@ -168,14 +197,14 @@ public abstract class LivingEntityMixin_Tracker extends EntityMixin_Tracker { method = "tickEffects", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/effect/MobEffectInstance;tick(Lnet/minecraft/world/entity/LivingEntity;Ljava/lang/Runnable;)Z" + target = "Lnet/minecraft/world/effect/MobEffectInstance;tickServer(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/LivingEntity;Ljava/lang/Runnable;)Z" ) ) private boolean impl$wrapEffectWithFrame( - final MobEffectInstance instance, final LivingEntity thisEntity, final Runnable runnable) { + final MobEffectInstance instance, final ServerLevel level, final LivingEntity thisEntity, final Runnable runnable) { try { PhaseTracker.getInstance().pushCause(instance); // push the PotionEffect - return instance.tick(thisEntity, runnable); + return instance.tickServer(level, thisEntity, runnable); } finally { PhaseTracker.getInstance().popCause(); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/item/PrimedTntMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/item/PrimedTntMixin_Tracker.java index 235de221041..aa658f8de06 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/item/PrimedTntMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/item/PrimedTntMixin_Tracker.java @@ -38,12 +38,13 @@ @Mixin(PrimedTnt.class) public abstract class PrimedTntMixin_Tracker extends EntityMixin_Tracker { - @Shadow @Nullable private LivingEntity owner; + @Shadow @Nullable public abstract LivingEntity shadow$getOwner(); @Override protected void tracker$populateDeathContextIfNeeded(final CauseStackManager.StackFrame frame, final EntityTickContext context) { - if (this.owner != null) { - frame.addContext(EventContextKeys.IGNITER, (Living) this.owner); + final var owner = this.shadow$getOwner(); + if (owner != null) { + frame.addContext(EventContextKeys.IGNITER, (Living) owner); } super.tracker$populateDeathContextIfNeeded(frame, context); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/player/PlayerMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/player/PlayerMixin_Tracker.java index 96792660301..da0e81c6005 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/player/PlayerMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/entity/player/PlayerMixin_Tracker.java @@ -24,15 +24,10 @@ */ package org.spongepowered.common.mixin.tracker.world.entity.player; -import net.minecraft.network.chat.Component; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.event.entity.DestructEntityEvent; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -42,13 +37,6 @@ @Mixin(Player.class) public abstract class PlayerMixin_Tracker extends LivingEntityMixin_Tracker { - //@formatter:off - @Shadow public abstract Component shadow$getDisplayName(); - @Shadow @Nullable public ItemEntity shadow$drop(ItemStack droppedItem, boolean dropAround, boolean traceItem) { - return null; // SHADOWED - } - //@formatter:on - @Inject(method = "die", at = @At("HEAD"), cancellable = true) public void impl$callDestructEntityDeath(final DamageSource cause, final CallbackInfo ci) { if (this.shadow$isEffectiveAi()) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/item/BoneMealItemMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/item/BoneMealItemMixin_Tracker.java index c38e4b355d1..14f8d2e9bde 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/item/BoneMealItemMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/item/BoneMealItemMixin_Tracker.java @@ -79,11 +79,12 @@ public abstract class BoneMealItemMixin_Tracker { return; } - final PhaseContext<@NonNull ?> current = PhaseTracker.getInstance().getPhaseContext(); + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(worldIn); + final PhaseContext<@NonNull ?> current = phaseTracker.getPhaseContext(); final boolean doesEvent = current.doesBlockEventTracking(); if (doesEvent) { // We can enter the new phase state. - try (final GrowablePhaseContext context = BlockPhase.State.GROWING.createPhaseContext(PhaseTracker.SERVER) + try (final GrowablePhaseContext context = BlockPhase.State.GROWING.createPhaseContext(phaseTracker) .provideItem(stack) .world(worldIn) .block(state) diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/item/ItemStackMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/item/ItemStackMixin_Tracker.java index 71009642869..f981aa4e7fb 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/item/ItemStackMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/item/ItemStackMixin_Tracker.java @@ -25,6 +25,7 @@ package org.spongepowered.common.mixin.tracker.world.item; import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; @@ -55,7 +56,7 @@ public abstract class ItemStackMixin_Tracker { final LivingEntity player, final Level levelIn, final BlockState stateIn, final BlockPos posIn, final Player playerIn ) { final boolean isServerLevel = !((LevelBridge) level).bridge$isFake(); - final PhaseContext<@NonNull ?> context = isServerLevel ? PhaseTracker.SERVER.getPhaseContext() : null; + final PhaseContext<@NonNull ?> context = isServerLevel ? PhaseTracker.getWorldInstance((ServerLevel) level).getPhaseContext() : null; try { if (isServerLevel) { context.capturePlayerUsingStackToBreakBlock((ServerPlayer) playerIn); diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/LevelAccessorMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/LevelAccessorMixin_Tracker.java deleted file mode 100644 index 4ae817d3e95..00000000000 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/LevelAccessorMixin_Tracker.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.mixin.tracker.world.level; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.storage.LevelData; -import net.minecraft.world.ticks.LevelTickAccess; -import net.minecraft.world.ticks.TickPriority; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; - -@Mixin(LevelAccessor.class) -public interface LevelAccessorMixin_Tracker { - - // @formatter:off - @Shadow LevelData shadow$getLevelData(); - @Shadow long shadow$nextSubTickCount(); - @Shadow LevelTickAccess shadow$getBlockTicks(); - @Shadow LevelTickAccess shadow$getFluidTicks(); - - // Shadow methods to be overridden in ServerLevelMixin_Tracker - @Shadow void shadow$scheduleTick(BlockPos pos, Block block, int $$2, TickPriority priority); - @Shadow void shadow$scheduleTick(BlockPos pos, Block block, int $$2); - @Shadow void shadow$scheduleTick(BlockPos pos, Fluid fluid, int $$2, TickPriority priority); - @Shadow void shadow$scheduleTick(BlockPos pos, Fluid fluid, int $$2); - // @formatter:on - -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/ExplosionMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/ServerExplosionMixin_Tracker.java similarity index 77% rename from src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/ExplosionMixin_Tracker.java rename to src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/ServerExplosionMixin_Tracker.java index 58603651fb9..382861e9121 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/ExplosionMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/ServerExplosionMixin_Tracker.java @@ -27,9 +27,11 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Explosion; import net.minecraft.world.level.Level; +import net.minecraft.world.level.ServerExplosion; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import org.checkerframework.checker.nullness.qual.NonNull; @@ -48,24 +50,27 @@ import java.util.function.BiConsumer; -@Mixin(Explosion.class) -public abstract class ExplosionMixin_Tracker { +@Mixin(ServerExplosion.class) +public abstract class ServerExplosionMixin_Tracker { - @WrapOperation(method = "finalizeExplosion", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/Block;popResource(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/item/ItemStack;)V")) + // TODO sounds & particles? + + @WrapOperation(method = "interactWithBlocks", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/Block;popResource(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/item/ItemStack;)V")) private void tracker$onPopResource(final Level level, final BlockPos pos, final ItemStack item, final Operation original) { - if (level.isClientSide) { - original.call(level, pos, item); - } - // else: This is built into the SpawnDestructBlocksEffect + // This is built into the SpawnDestructBlocksEffect } - @WrapOperation(method = "finalizeExplosion", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;onExplosionHit(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/Explosion;Ljava/util/function/BiConsumer;)V")) - private void tracker$onExplosionHit(final BlockState block, final Level level, final BlockPos pos, final Explosion explosion, final BiConsumer dropConsumer, final Operation original) { - if (level.isClientSide || explosion.getBlockInteraction() == Explosion.BlockInteraction.TRIGGER_BLOCK) { + @WrapOperation(method = "interactWithBlocks", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;onExplosionHit(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/Explosion;Ljava/util/function/BiConsumer;)V")) + private void tracker$onExplosionHit(final BlockState block, final ServerLevel level, + final BlockPos pos, final Explosion explosion, final BiConsumer dropConsumer, final Operation original) { + if (explosion.getBlockInteraction() == Explosion.BlockInteraction.TRIGGER_BLOCK) { original.call(block, level, pos, explosion, dropConsumer); return; } + // TODO addOrAppendStack? ItemStack pre merging final PhaseContext<@NonNull ?> context = PhaseTracker.getInstance().getPhaseContext(); ((TrackedWorldBridge) level).bridge$startBlockChange(pos, Blocks.AIR.defaultBlockState(), 3) .ifPresent(builder -> { diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/BlockMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/BlockMixin_Tracker.java index 7ebf06dd4df..e4b0049fb86 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/BlockMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/BlockMixin_Tracker.java @@ -39,6 +39,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.common.bridge.RegistryBackedTrackableBridge; import org.spongepowered.common.bridge.world.level.block.TrackableBlockBridge; import org.spongepowered.common.config.SpongeGameConfigs; @@ -51,14 +52,9 @@ @Mixin(Block.class) public abstract class BlockMixin_Tracker implements TrackableBlockBridge, RegistryBackedTrackableBridge { - private final boolean tracker$hasNeighborLogicOverridden = ReflectionUtil.isNeighborChangedDeclared(this.getClass()); + @Unique private final boolean tracker$hasEntityInsideLogicOverridden = ReflectionUtil.isEntityInsideDeclared(this.getClass()); - @Override - public boolean bridge$overridesNeighborNotificationLogic() { - return this.tracker$hasNeighborLogicOverridden; - } - @Override public boolean bridge$hasEntityInsideLogic() { return this.tracker$hasEntityInsideLogicOverridden; @@ -91,7 +87,7 @@ public abstract class BlockMixin_Tracker implements TrackableBlockBridge, Regist private static void tracker$logBlockDrops(final Level worldIn, final BlockPos pos, final BlockState state, final @Nullable BlockEntity tileEntity, final Runnable runnable) { - final PhaseTracker tracker = PhaseTracker.SERVER; + final PhaseTracker tracker = PhaseTracker.getWorldInstance(worldIn); if (tracker.onSidedThread()) { if (tracker.getPhaseContext().isRestoring()) { return; diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/DispenserBlockMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/DispenserBlockMixin_Tracker.java index c0f20fb7eb3..1d8c62b95a3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/DispenserBlockMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/DispenserBlockMixin_Tracker.java @@ -68,7 +68,7 @@ public abstract class DispenserBlockMixin_Tracker { final SpongeBlockSnapshot spongeBlockSnapshot = ((TrackedWorldBridge) worldIn).bridge$createSnapshot(state, pos, BlockChangeFlags.ALL); final LevelChunkBridge mixinChunk = (LevelChunkBridge) worldIn.getChunkAt(pos); try (final @Nullable PhaseContext<@NonNull ?> context = BlockPhase.State.DISPENSE - .createPhaseContext(PhaseTracker.SERVER) + .createPhaseContext(PhaseTracker.getWorldInstance(worldIn)) .source(spongeBlockSnapshot) .creator(() -> mixinChunk.bridge$getBlockCreatorUUID(pos)) .notifier(() -> mixinChunk.bridge$getBlockNotifierUUID(pos))) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/LeavesBlockMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/LeavesBlockMixin_Tracker.java index 2836dbcfdfa..057d59040bd 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/LeavesBlockMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/LeavesBlockMixin_Tracker.java @@ -89,7 +89,7 @@ public abstract class LeavesBlockMixin_Tracker extends BlockMixin_Tracker { public void tracker$onRandomTick(final net.minecraft.world.level.block.state.BlockState state, final net.minecraft.server.level.ServerLevel worldIn, final BlockPos pos, final RandomSource random, final Operation original) { if (this.shadow$decaying(state)) { if (!((LevelBridge) worldIn).bridge$isFake()) { - try (final PhaseContext<@NonNull ?> context = BlockPhase.State.BLOCK_DECAY.createPhaseContext(PhaseTracker.SERVER) + try (final PhaseContext<@NonNull ?> context = BlockPhase.State.BLOCK_DECAY.createPhaseContext(PhaseTracker.getWorldInstance(worldIn)) .source(new SpongeLocatableBlockBuilder() .world((ServerWorld) worldIn) .position(pos.getX(), pos.getY(), pos.getZ()) diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/SaplingBlockMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/SaplingBlockMixin_Tracker.java index f2bf8151a03..605c5f04a62 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/SaplingBlockMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/SaplingBlockMixin_Tracker.java @@ -57,11 +57,12 @@ public class SaplingBlockMixin_Tracker { return instance.growTree(level, generator, pos, state, random); } - if (PhaseTracker.getInstance().getPhaseContext() instanceof GrowablePhaseContext) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance(level); + if (phaseTracker.getPhaseContext() instanceof GrowablePhaseContext) { instance.growTree(level, generator, pos, state, random); - } else if (PhaseTracker.getInstance().getPhaseContext().doesBlockEventTracking()) { + } else if (phaseTracker.getPhaseContext().doesBlockEventTracking()) { // We can enter the new phase state. - try (GrowablePhaseContext context = BlockPhase.State.GROWING.createPhaseContext(PhaseTracker.SERVER) + try (GrowablePhaseContext context = BlockPhase.State.GROWING.createPhaseContext(phaseTracker) .world(level) .block(state) .pos(pos)) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/entity/BlockEntityTypeMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/entity/BlockEntityTypeMixin_Tracker.java index ca60ab49727..0ca1c2d58b7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/entity/BlockEntityTypeMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/entity/BlockEntityTypeMixin_Tracker.java @@ -28,8 +28,6 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.entity.BlockEntityType; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.bridge.RegistryBackedTrackableBridge; import org.spongepowered.common.config.SpongeGameConfigs; import org.spongepowered.common.config.tracker.TrackerCategory; @@ -37,21 +35,6 @@ @Mixin(BlockEntityType.class) public abstract class BlockEntityTypeMixin_Tracker implements RegistryBackedTrackableBridge> { - @Redirect(method = "register", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/core/Registry;register(Lnet/minecraft/core/Registry;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;" - ) - ) - private static Object impl$initializeTrackerState(final Registry registry, final String key, final Object toRegister) { - final Object registered = Registry.register(registry, key, toRegister); - - final RegistryBackedTrackableBridge> trackableBridge = (RegistryBackedTrackableBridge>) toRegister; - trackableBridge.bridge$refreshTrackerStates(); - - return registered; - } - @Override public TrackerCategory bridge$trackerCategory() { return SpongeGameConfigs.getTracker().get().blockEntity; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/MainMixin.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/entity/BlockEntityTypesMixin_Tracker.java similarity index 54% rename from src/mixins/java/org/spongepowered/common/mixin/core/server/MainMixin.java rename to src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/entity/BlockEntityTypesMixin_Tracker.java index 640f53b3f3a..d60af5e6068 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/MainMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/block/entity/BlockEntityTypesMixin_Tracker.java @@ -22,27 +22,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.server; +package org.spongepowered.common.mixin.tracker.world.level.block.entity; -import net.minecraft.server.Main; -import net.minecraft.server.packs.repository.PackRepository; -import net.minecraft.server.packs.repository.ServerPacksSource; -import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.BlockEntityTypes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.bridge.RegistryBackedTrackableBridge; +@Mixin(BlockEntityTypes.class) +public abstract class BlockEntityTypesMixin_Tracker { -@Mixin(Main.class) -public abstract class MainMixin { + @Redirect(method = "register", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/core/Registry;register(Lnet/minecraft/core/Registry;Lnet/minecraft/resources/ResourceKey;Ljava/lang/Object;)Ljava/lang/Object;" + ) + ) + private static Object impl$initializeTrackerState(final Registry registry, final ResourceKey key, final Object toRegister) { + final Object registered = Registry.register(registry, key, toRegister); - @Redirect(method = "main", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/repository/ServerPacksSource;createPackRepository(Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;)Lnet/minecraft/server/packs/repository/PackRepository;")) - private static PackRepository impl$configurePackRepository(LevelStorageSource.LevelStorageAccess levelSave) { - final var lifecycle = Launch.instance().lifecycle(); - lifecycle.establishDataProviders(); - lifecycle.callRegisterDataEvent(); - return ServerPacksSource.createPackRepository(levelSave); - } + final RegistryBackedTrackableBridge> trackableBridge = (RegistryBackedTrackableBridge>) toRegister; + trackableBridge.bridge$refreshTrackerStates(); + return registered; + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/chunk/LevelChunkMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/chunk/LevelChunkMixin_Tracker.java index 1d206f412e2..4b9c85c1110 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/chunk/LevelChunkMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/chunk/LevelChunkMixin_Tracker.java @@ -80,7 +80,7 @@ public abstract class LevelChunkMixin_Tracker extends ChunkAccessMixin_Tracker i private @MonotonicNonNull PhaseContext<@NonNull ?> tracker$postProcessContext = null; @Inject(method = "setBlockState", at = @At("HEAD"), cancellable = true) - private void tracker$sanityCheckServerWorldSetBlockState(final BlockPos pos, final BlockState state, final boolean isMoving, + private void tracker$sanityCheckServerWorldSetBlockState(final BlockPos pos, final BlockState state, final int flag, final CallbackInfoReturnable cir ) { if (!((LevelBridge) this.level).bridge$isFake()) { @@ -130,7 +130,7 @@ public abstract class LevelChunkMixin_Tracker extends ChunkAccessMixin_Tracker i } // Sponge Start - Build out the BlockTransaction - final PhaseContext<@NonNull ?> context = PhaseTracker.getInstance().getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance((ServerLevel) this.level).getPhaseContext(); final @Nullable BlockEntity existing = this.shadow$getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); // Build a transaction maybe? final WeakReference ref = new WeakReference<>((ServerLevel) this.level); @@ -165,11 +165,12 @@ public abstract class LevelChunkMixin_Tracker extends ChunkAccessMixin_Tracker i @Inject(method = "postProcessGeneration", at = @At("HEAD")) private void tracker$startChunkPostProcess(final CallbackInfo ci) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) this.level); if (this.tracker$postProcessContext != null) { - PhasePrinter.printMessageWithCaughtException(PhaseTracker.SERVER, "Expected to not have a chunk post process", "Chunk Post Process has not completed!", GenerationPhase.State.CHUNK_LOADING, this.tracker$postProcessContext, new NullPointerException("spongecommon.ChunkMixin_Tracker:tracker$postProcessContext is Null")); + PhasePrinter.printMessageWithCaughtException(phaseTracker, "Expected to not have a chunk post process", "Chunk Post Process has not completed!", GenerationPhase.State.CHUNK_LOADING, this.tracker$postProcessContext, new NullPointerException("spongecommon.ChunkMixin_Tracker:tracker$postProcessContext is Null")); this.tracker$postProcessContext.close(); } - this.tracker$postProcessContext = GenerationPhase.State.CHUNK_LOADING.createPhaseContext(PhaseTracker.SERVER) + this.tracker$postProcessContext = GenerationPhase.State.CHUNK_LOADING.createPhaseContext(phaseTracker) .chunk((LevelChunk) (Object) this) .world((ServerLevel) this.level) .buildAndSwitch(); @@ -205,10 +206,11 @@ public abstract class LevelChunkMixin_Tracker extends ChunkAccessMixin_Tracker i @SuppressWarnings({"rawtypes", "unchecked"}) @Redirect(method = "unpackTicks", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/ticks/LevelChunkTicks;unpack(J)V")) private void tracker$wrapRescheduledTicks(final LevelChunkTicks instance, final long gameTime) { - if (!PhaseTracker.SERVER.onSidedThread()) { + final PhaseTracker phaseTracker = PhaseTracker.getWorldInstance((ServerLevel) this.level); + if (!phaseTracker.onSidedThread()) { return; } - try (final ChunkLoadContext context = GenerationPhase.State.CHUNK_LOADING.createPhaseContext(PhaseTracker.SERVER)) { + try (final ChunkLoadContext context = GenerationPhase.State.CHUNK_LOADING.createPhaseContext(phaseTracker)) { context.chunk((LevelChunk) (Object) this); context.buildAndSwitch(); instance.unpack(gameTime); diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/chunk/storage/ChunkSerializerMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/chunk/storage/SerializableChunkDataMixin_Tracker.java similarity index 52% rename from src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/chunk/storage/ChunkSerializerMixin_Tracker.java rename to src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/chunk/storage/SerializableChunkDataMixin_Tracker.java index e05192aa1b1..d6148c1bde7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/chunk/storage/ChunkSerializerMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/chunk/storage/SerializableChunkDataMixin_Tracker.java @@ -27,48 +27,51 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.ai.village.poi.PoiManager; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.storage.ChunkSerializer; -import net.minecraft.world.level.chunk.storage.RegionStorageInfo; +import net.minecraft.world.level.chunk.storage.SerializableChunkData; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge; +import org.spongepowered.common.bridge.world.level.chunk.storage.SerializableChunkDataBridge; import org.spongepowered.common.entity.PlayerTracker; import org.spongepowered.common.util.Constants; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.function.BiConsumer; -@Mixin(ChunkSerializer.class) -public abstract class ChunkSerializerMixin_Tracker { +@Mixin(SerializableChunkData.class) +public abstract class SerializableChunkDataMixin_Tracker implements SerializableChunkDataBridge { - @Inject(method = "write", at = @At(value = "RETURN")) - private static void impl$writeSpongeLevelData(final ServerLevel param0, final ChunkAccess param1, final CallbackInfoReturnable cir) { - if (!(param1 instanceof LevelChunk)) { - return; - } - final LevelChunkBridge chunk = (LevelChunkBridge) param1; + private Map tracker$shortPlayerPos = null; + private Map tracker$intPlayerPos = null; + + @Override + public void bridge$setTrackerData(final LevelChunk chunkAccess) { + final LevelChunkBridge chunk = (LevelChunkBridge) chunkAccess; if (!chunk.bridge$getTrackedShortPlayerPositions().isEmpty() || !chunk.bridge$getTrackedIntPlayerPositions().isEmpty()) { - final CompoundTag level = cir.getReturnValue(); - final CompoundTag trackedNbt = new CompoundTag(); - final ListTag positions = new ListTag(); - trackedNbt.put(Constants.Sponge.SPONGE_BLOCK_POS_TABLE, positions); - level.put(Constants.Sponge.Data.V2.SPONGE_DATA, trackedNbt); + this.tracker$shortPlayerPos = chunk.bridge$getTrackedShortPlayerPositions(); + this.tracker$intPlayerPos = chunk.bridge$getTrackedIntPlayerPositions(); + } + } - ChunkSerializerMixin_Tracker.impl$writeMap(positions, chunk.bridge$getTrackedShortPlayerPositions(), (nbt, pos) -> nbt.putShort("pos", pos)); - ChunkSerializerMixin_Tracker.impl$writeMap(positions, chunk.bridge$getTrackedIntPlayerPositions(), (nbt, pos) -> nbt.putInt("ipos", pos)); + @SuppressWarnings("deprecation") + @Override + public void bridge$writeTrackerData(final CompoundTag level) { + if (this.tracker$intPlayerPos == null && this.tracker$shortPlayerPos == null) { + return; } + final CompoundTag trackedNbt = new CompoundTag(); + final ListTag positions = new ListTag(); + trackedNbt.put(Constants.Sponge.SPONGE_BLOCK_POS_TABLE, positions); + level.put(Constants.Sponge.Data.V2.SPONGE_DATA, trackedNbt); + + SerializableChunkDataMixin_Tracker.tracker$writeMap(positions, this.tracker$shortPlayerPos, (nbt, pos) -> nbt.putShort("pos", pos)); + SerializableChunkDataMixin_Tracker.tracker$writeMap(positions, this.tracker$intPlayerPos, (nbt, pos) -> nbt.putInt("ipos", pos)); } - private static void impl$writeMap(final ListTag positions, final Map map, final BiConsumer consumer) { + + private static void tracker$writeMap(final ListTag positions, final Map map, final BiConsumer consumer) { for (final Map.Entry mapEntry : map.entrySet()) { final T pos = mapEntry.getKey(); final int ownerUniqueIdIndex = mapEntry.getValue().creatorindex; @@ -88,40 +91,46 @@ public abstract class ChunkSerializerMixin_Tracker { } } - @Redirect(method = "read", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/ChunkAccess;setLightCorrect(Z)V")) - private static void impl$readSpongeLevelData(final ChunkAccess chunkAccess, final boolean var1, final ServerLevel param0, final PoiManager param2, final RegionStorageInfo $$2, final ChunkPos param3, final CompoundTag param4) { - if (!(chunkAccess instanceof LevelChunk)) { - return; - } - final CompoundTag spongeData = param4.getCompound(Constants.Sponge.Data.V2.SPONGE_DATA); + @SuppressWarnings("deprecation") + @Override + public void bridge$parseTrackerData(final CompoundTag fullTag) { + final Optional spongeData = fullTag.getCompound(Constants.Sponge.Data.V2.SPONGE_DATA); + this.tracker$intPlayerPos = new HashMap<>(); + this.tracker$shortPlayerPos = new HashMap<>(); + if (spongeData.isEmpty()) { return; } - final Map trackedIntPlayerPositions = new HashMap<>(); - final Map trackedShortPlayerPositions = new HashMap<>(); - final ListTag list = spongeData.getList(Constants.Sponge.SPONGE_BLOCK_POS_TABLE, 10); - final LevelChunkBridge chunk = (LevelChunkBridge) chunkAccess; - for (final Tag tag : list) { + final Optional list = spongeData.get().getList(Constants.Sponge.SPONGE_BLOCK_POS_TABLE); + if (list.isEmpty()) { + return; + } + for (final Tag tag : list.get()) { final PlayerTracker tracker = new PlayerTracker(); final CompoundTag data = (CompoundTag) tag; final boolean isShortPos = data.contains("pos"); if (data.contains("owner")) { - tracker.creatorindex = data.getInt("owner"); + tracker.creatorindex = data.getIntOr("owner", 0); } if (data.contains("notifier")) { - tracker.notifierIndex = data.getInt("notifier"); + tracker.notifierIndex = data.getIntOr("notifier", 0); } if (tracker.notifierIndex != -1 || tracker.creatorindex != -1) { if (isShortPos) { - trackedShortPlayerPositions.put(data.getShort("pos"), tracker); + this.tracker$shortPlayerPos.put(data.getShortOr("pos", (short) 0), tracker); } else { - trackedIntPlayerPositions.put(data.getInt("ipos"), tracker); + this.tracker$intPlayerPos.put(data.getIntOr("ipos", 0), tracker); } } } - chunk.bridge$setTrackedIntPlayerPositions(trackedIntPlayerPositions); - chunk.bridge$setTrackedShortPlayerPositions(trackedShortPlayerPositions); } + + @Override + public void bridge$readTrackerDataFrom(final LevelChunk levelChunk) { + final LevelChunkBridge chunk = (LevelChunkBridge) levelChunk; + chunk.bridge$setTrackedIntPlayerPositions(this.tracker$intPlayerPos); + chunk.bridge$setTrackedShortPlayerPositions(this.tracker$shortPlayerPos); + } + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/dimension/end/EndDragonFightMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/dimension/end/EndDragonFightMixin_Tracker.java index b0ebcce7ac3..b25739c6ebf 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/dimension/end/EndDragonFightMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/dimension/end/EndDragonFightMixin_Tracker.java @@ -24,6 +24,8 @@ */ package org.spongepowered.common.mixin.tracker.world.level.dimension.end; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.RandomSource; @@ -31,8 +33,8 @@ import net.minecraft.world.entity.boss.enderdragon.EnderDragon; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.dimension.end.DragonRespawnAnimation; -import net.minecraft.world.level.dimension.end.EndDragonFight; +import net.minecraft.world.level.dimension.end.DragonRespawnStage; +import net.minecraft.world.level.dimension.end.EnderDragonFight; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -46,25 +48,26 @@ import java.util.List; -@Mixin(EndDragonFight.class) +@Mixin(EnderDragonFight.class) public abstract class EndDragonFightMixin_Tracker { // @formatter:off - @Shadow protected abstract EnderDragon shadow$createNewDragon(); + @Shadow private ServerLevel level; // @formatter:on - @Redirect(method = "lambda$spawnNewGateway$2", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/feature/ConfiguredFeature;place(Lnet/minecraft/world/level/WorldGenLevel;Lnet/minecraft/world/level/chunk/ChunkGenerator;Lnet/minecraft/util/RandomSource;Lnet/minecraft/core/BlockPos;)Z")) - private boolean tracker$switchToFeatureState(final ConfiguredFeature configuredFeature, final WorldGenLevel worldIn, final ChunkGenerator generator, + @Redirect(method = "lambda$spawnNewGateway$1", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/feature/ConfiguredFeature;place(Lnet/minecraft/world/level/WorldGenLevel;Lnet/minecraft/world/level/chunk/ChunkGenerator;Lnet/minecraft/util/RandomSource;Lnet/minecraft/core/BlockPos;)Z")) + private boolean tracker$switchToFeatureState( + final ConfiguredFeature configuredFeature, final WorldGenLevel worldIn, final ChunkGenerator generator, final RandomSource rand, final BlockPos pos ) { - try (final FeaturePhaseContext context = GenerationPhase.State.FEATURE_PLACEMENT.createPhaseContext(PhaseTracker.SERVER)) { + try (final FeaturePhaseContext context = GenerationPhase.State.FEATURE_PLACEMENT.createPhaseContext(PhaseTracker.getWorldInstance(worldIn.getLevel()))) { context - .world((ServerLevel) worldIn) - .generator(generator) - .feature(configuredFeature.feature()) - .origin(pos) + .world((ServerLevel) worldIn) + .generator(generator) + .feature(configuredFeature.feature()) + .origin(pos) ; context.buildAndSwitch(); @@ -72,30 +75,32 @@ public abstract class EndDragonFightMixin_Tracker { } } - @Redirect(method = "tick()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/dimension/end/DragonRespawnAnimation;tick(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/dimension/end/EndDragonFight;Ljava/util/List;ILnet/minecraft/core/BlockPos;)V")) - private void tracker$switchToSpawnDragonState(final DragonRespawnAnimation dragonSpawnState, final ServerLevel worldIn, - final EndDragonFight manager, final List crystals, int respawnStateTicks, final BlockPos exitPortalLocation) { - try (final SpawnDragonContext context = DragonPhase.State.SPAWN_DRAGON.createPhaseContext(PhaseTracker.SERVER)) { + @WrapOperation(method = "tick()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/dimension/end/DragonRespawnStage;tick(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/dimension/end/EnderDragonFight;Ljava/util/List;I)V")) + private void tracker$switchToSpawnDragonState( + final DragonRespawnStage instance, final ServerLevel serverLevel, final EnderDragonFight manager, + final List endCrystals, int i, final Operation original + ) { + try (final SpawnDragonContext context = DragonPhase.State.SPAWN_DRAGON.createPhaseContext(PhaseTracker.getWorldInstance(serverLevel))) { context - .manager(manager) - .setIsRespawn(true) - .buildAndSwitch() + .manager(manager) + .setIsRespawn(true) + .buildAndSwitch() ; - ++respawnStateTicks; - dragonSpawnState.tick(worldIn, manager, crystals, respawnStateTicks, exitPortalLocation); + ++i; + original.call(instance, serverLevel, manager, endCrystals, i); } } - @Redirect(method = "findOrCreateDragon", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/dimension/end/EndDragonFight;createNewDragon()Lnet/minecraft/world/entity/boss/enderdragon/EnderDragon;")) - private EnderDragon tracker$switchToSpawnDragonState(final EndDragonFight manager) { - try (final SpawnDragonContext context = DragonPhase.State.SPAWN_DRAGON.createPhaseContext(PhaseTracker.SERVER)) { + @WrapOperation(method = "findOrCreateDragon", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/dimension/end/EnderDragonFight;createNewDragon()Lnet/minecraft/world/entity/boss/enderdragon/EnderDragon;")) + private EnderDragon tracker$switchToSpawnDragonState(final EnderDragonFight manager, final Operation original) { + try (final SpawnDragonContext context = DragonPhase.State.SPAWN_DRAGON.createPhaseContext(PhaseTracker.getWorldInstance(this.level))) { context - .manager(manager) - .setIsRespawn(false) - .buildAndSwitch() + .manager(manager) + .setIsRespawn(false) + .buildAndSwitch() ; - return this.shadow$createNewDragon(); + return original.call(manager); } } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/redstone/NeighborUpdaterMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/redstone/NeighborUpdaterMixin_Tracker.java index e16d22970e2..f34827ef39d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/redstone/NeighborUpdaterMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/redstone/NeighborUpdaterMixin_Tracker.java @@ -31,6 +31,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.redstone.NeighborUpdater; +import net.minecraft.world.level.redstone.Orientation; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; @@ -50,32 +51,26 @@ public interface NeighborUpdaterMixin_Tracker { /** * Logs a transaction to the {@link PhaseContext} for neighbor notifications. Otherwise, * we will fail to create notification events in the API. This avoids a redirect or overwrite - * - * @param level - * @param targetState - * @param targetPos - * @param fromBlock - * @param fromPos - * @param movement - * @param ci */ @Inject(method = "executeUpdate", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/level/block/state/BlockState;handleNeighborChanged(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/Block;Lnet/minecraft/core/BlockPos;Z)V" + target = "Lnet/minecraft/world/level/block/state/BlockState;handleNeighborChanged(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/Block;Lnet/minecraft/world/level/redstone/Orientation;Z)V" )) - private static void tracker$wrapNeighborUpdateInSideEffect(Level level, BlockState targetState, BlockPos targetPos, Block fromBlock, BlockPos fromPos, boolean movement, CallbackInfo ci) { + private static void tracker$wrapNeighborUpdateInSideEffect(Level level, BlockState targetState, BlockPos targetPos, Block fromBlock, @Nullable Orientation orientation, boolean movement, CallbackInfo ci) { // Sponge start - prepare notification - final PhaseContext<@NonNull ?> peek = PhaseTracker.getInstance().getPhaseContext(); if (!(level instanceof ServerLevel serverLevel)) { return; } + final PhaseContext<@NonNull ?> peek = PhaseTracker.getWorldInstance(serverLevel).getPhaseContext(); final var targetChunk = serverLevel.getChunkAt(targetPos); final Supplier worldSupplier = VolumeStreamUtils.createWeaklyReferencedSupplier(serverLevel, "ServerWorld"); final net.minecraft.world.level.block.entity.@Nullable BlockEntity existingTile = targetChunk.getBlockEntity( targetPos, LevelChunk.EntityCreationType.CHECK ); + + var fromPos = targetPos; // TODO fromPos is gone THIS IS ALMOST CERTAINLY WRONG peek.getTransactor().logNeighborNotification(worldSupplier, fromPos, fromBlock, targetPos, targetState, existingTile); peek.associateNeighborStateNotifier(fromPos, targetState.getBlock(), targetPos, serverLevel, PlayerTracker.Type.NOTIFIER); diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/ticks/LevelTicksMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/ticks/LevelTicksMixin_Tracker.java index 9c1ef51de39..36a3eac4cd2 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/ticks/LevelTicksMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/ticks/LevelTicksMixin_Tracker.java @@ -58,7 +58,7 @@ public abstract class LevelTicksMixin_Tracker { final var thisScheduledTick = this.alreadyRunThisTick.getLast(); final CreatorTrackedBridge bridge = (CreatorTrackedBridge) (Object) thisScheduledTick; try (final var context = GenerationPhase.State.DEFERRED_SCHEDULED_UPDATE.createPhaseContext( - PhaseTracker.SERVER) + PhaseTracker.getWorldInstance()) .source(this) .creator(bridge::tracker$getCreatorUUID) .notifier(bridge::tracker$getNotifierUUID) diff --git a/src/mixins/resources/mixins.sponge.api.json b/src/mixins/resources/mixins.sponge.api.json index 22d8999c7b0..e57abfee3be 100644 --- a/src/mixins/resources/mixins.sponge.api.json +++ b/src/mixins/resources/mixins.sponge.api.json @@ -9,35 +9,6 @@ "data.persistence.DataContainerMixin_API", "entity.ai.goal.AbstractGoalMixin_API", "item.merchant.MerchantMixin_API", - "itemstacklike.advancement.DisplayInfo_BuilderMixin_ItemStackLike", - "itemstacklike.block.entity.JukeboxMixin_ItemStackLike", - "itemstacklike.event.cause.entity.damage.DamageModifier_BuilderMixin_ItemStackLike", - "itemstacklike.item.enchantment.EnchantmentTypeMixin_ItemStackLike", - "itemstacklike.item.inventory.ArmorEquipableMixin_ItemStackLike", - "itemstacklike.item.inventory.ContainerMixin_ItemStackLike", - "itemstacklike.item.inventory.EquipableMixin_ItemStackLike", - "itemstacklike.item.inventory.InventoryMixin_ItemStackLike", - "itemstacklike.item.inventory.SlotMixin_ItemStackLike", - "itemstacklike.item.inventory.equipment.EquipmentInventoryMixin_ItemStackLike", - "itemstacklike.item.inventory.slot.FilteringSlotMixin_ItemStackLike", - "itemstacklike.item.inventory.slot.SidedSlotMixin_ItemStackLike", - "itemstacklike.item.inventory.transaction.InventoryTransactionResult_BuilderMixin_ItemStackLike", - "itemstacklike.item.inventory.transaction.SlotTransactionMixin_ItemStackLike", - "itemstacklike.item.inventory.type.GridInventoryMixin_ItemStackLike", - "itemstacklike.item.inventory.type.Inventory2DMixin_ItemStackLike", - "itemstacklike.item.inventory.type.ViewableInventory_Builder_DummyStepMixin_ItemStackLike", - "itemstacklike.item.merchant.TradeOffer_BuilderMixin_ItemStackLike", - "itemstacklike.item.recipe.RecipeManagerMixin_ItemStackLike", - "itemstacklike.item.recipe.cooking.CookingRecipe_Builder_ResultStepMixin_ItemStackLike", - "itemstacklike.item.recipe.cooking.CookingRecipeMixin_ItemStackLike", - "itemstacklike.item.recipe.crafting.Ingredient_BuilderMixin_ItemStackLike", - "itemstacklike.item.recipe.crafting.IngredientMixin_ItemStackLike", - "itemstacklike.item.recipe.crafting.RecipeInput_FactoryMixin_ItemStackLike", - "itemstacklike.item.recipe.crafting.ShapedCraftingRecipe_Builder_ResultStepMixin_ItemStackLike", - "itemstacklike.item.recipe.crafting.ShapelessCraftingRecipe_Builder_ResultStepMixin_ItemStackLike", - "itemstacklike.item.recipe.crafting.SpecialCraftingRecipe_Builder_ResultStepMixin_ItemStackLike", - "itemstacklike.item.recipe.single.StoneCutterRecipe_Builder_ResultStepMixin_ItemStackLike", - "itemstacklike.item.recipe.smithing.SmithingRecipe_Builder_ResultStepMixin_ItemStackLike", "minecraft.advancements.AdvancementMixin_API", "minecraft.advancements.AdvancementNodeMixin_API", "minecraft.advancements.AdvancementProgressMixin_API", @@ -47,7 +18,7 @@ "minecraft.advancements.CriterionTriggerInstanceMixin_API", "minecraft.advancements.CriterionTriggerMixin_API", "minecraft.advancements.DisplayInfoMixin_API", - "minecraft.block.AbstractBlockMixin_API", + "minecraft.block.BlockBehaviourMixin_API", "minecraft.block.BlockMixin_API", "minecraft.block.BlockSoundGroupMixin_API", "minecraft.commands.CommandSourceStackMixin_API", @@ -55,8 +26,10 @@ "minecraft.commands.arguments.selector.EntitySelectorParserMixin_API", "minecraft.commands.synchronization.SuggestionProviders_WrapperMixin_API", "minecraft.core.MappedRegistryMixin_API", + "minecraft.core.RegistryMixin", "minecraft.core.particles.ParticleTypeMixin_API", "minecraft.entity.boss.WitherEntityMixin_API", + "minecraft.item.AirItemMixin_API", "minecraft.item.BlockItemMixin_API", "minecraft.map.MapDecorationMixin_API", "minecraft.map.MapDecorationTypeMixin_API", @@ -70,7 +43,7 @@ "minecraft.network.protocol.status.ServerStatus_PlayersMixin_API", "minecraft.network.protocol.status.ServerStatus_VersionMixin_API", "minecraft.network.protocol.status.ServerStatusMixin_API", - "minecraft.resources.ResourceLocationMixin_API", + "minecraft.resources.IdentifierMixin_API", "minecraft.server.MinecraftServerMixin_API", "minecraft.server.ServerScoreboardMixin_API", "minecraft.server.level.ChunkMapMixin_API", @@ -90,6 +63,7 @@ "minecraft.server.packs.repository.PackCompatibilityMixin_API", "minecraft.server.packs.repository.PackMixin_API", "minecraft.server.packs.repository.PackRepositoryMixin_API", + "minecraft.server.packs.resources.MultiPackResourceManagerMixin_API", "minecraft.server.packs.resources.ResourceManagerMixin_API", "minecraft.server.players.BanListEntryMixin_API", "minecraft.server.players.GameProfileCacheMixin_API", @@ -113,6 +87,9 @@ "minecraft.world.InteractionHandMixin_API", "minecraft.world.IWorldGenerationBaseReaderMixin_API", "minecraft.world.IWorldGenerationReaderMixin_API", + "minecraft.world.attribute.AmbientAdditionsSettingsMixin_API", + "minecraft.world.attribute.AmbientMoodSettingsMixin_API", + "minecraft.world.attribute.AmbientParticleSettingsMixin_API", "minecraft.world.damagesource.DamageEffectsMixin_API", "minecraft.world.damagesource.DamageScalingMixin_API", "minecraft.world.damagesource.DamageSourceMixin_API", @@ -131,8 +108,6 @@ "minecraft.world.entity.EntityMixin_API", "minecraft.world.entity.EntityTypeMixin_API", "minecraft.world.entity.ExperienceOrbMixin_API", - "minecraft.world.entity.FlyingMobMixin_API", - "minecraft.world.entity.GlowSquidMixin_API", "minecraft.world.entity.HumanoidArmMixin_API", "minecraft.world.entity.InteractionMixin_API", "minecraft.world.entity.LeashableMixin_API", @@ -146,7 +121,6 @@ "minecraft.world.entity.OwnableEntityMixin_API", "minecraft.world.entity.PathfinderMobMixin_API", "minecraft.world.entity.PortalProcessorMixin_API", - "minecraft.world.entity.PowerableMobMixin_API", "minecraft.world.entity.SaddleableMixin_API", "minecraft.world.entity.TamableAnimalMixin_API", "minecraft.world.entity.TargetingMixin_API", @@ -169,66 +143,79 @@ "minecraft.world.entity.ai.goal.target.TargetGoalMixin_API", "minecraft.world.entity.ambient.AmbientCreatureMixin_API", "minecraft.world.entity.ambient.BatMixin_API", - "minecraft.world.entity.animal.AbstractFishMixin_API", - "minecraft.world.entity.animal.AbstractGolemMixin_API", - "minecraft.world.entity.animal.AbstractSchoolingFishMixin_API", + "minecraft.world.entity.animal.cow.AbstractCowMixin_API", + "minecraft.world.entity.animal.fish.AbstractFishMixin_API", + "minecraft.world.entity.animal.fish.AbstractSchoolingFishMixin_API", + "minecraft.world.entity.animal.AgeableWaterCreatureMixin_API", "minecraft.world.entity.animal.AnimalMixin_API", - "minecraft.world.entity.animal.BeeMixin_API", - "minecraft.world.entity.animal.CatMixin_API", - "minecraft.world.entity.animal.CatVariantMixin_API", - "minecraft.world.entity.animal.ChickenMixin_API", - "minecraft.world.entity.animal.CodMixin_API", - "minecraft.world.entity.animal.CowMixin_API", - "minecraft.world.entity.animal.DolphinMixin_API", - "minecraft.world.entity.animal.FlyingAnimalMixin_API", - "minecraft.world.entity.animal.Fox_TypeMixin_API", - "minecraft.world.entity.animal.FoxMixin_API", - "minecraft.world.entity.animal.FrogVariantMixin_API", - "minecraft.world.entity.animal.IronGolemMixin_API", - "minecraft.world.entity.animal.MushroomCow_MushroomTypeMixin_API", - "minecraft.world.entity.animal.MushroomCowMixin_API", - "minecraft.world.entity.animal.OcelotMixin_API", - "minecraft.world.entity.animal.Panda_GeneMixin_API", - "minecraft.world.entity.animal.PandaMixin_API", - "minecraft.world.entity.animal.Parrot_VariantMixin_API", - "minecraft.world.entity.animal.ParrotMixin_API", - "minecraft.world.entity.animal.PigMixin_API", - "minecraft.world.entity.animal.PolarBearMixin_API", - "minecraft.world.entity.animal.PufferfishMixin_API", - "minecraft.world.entity.animal.Rabbit_VariantMixin_API", - "minecraft.world.entity.animal.RabbitMixin_API", - "minecraft.world.entity.animal.SalmonMixin_API", - "minecraft.world.entity.animal.SheepMixin_API", - "minecraft.world.entity.animal.ShoulderRidingEntityMixin_API", - "minecraft.world.entity.animal.SnowGolemMixin_API", - "minecraft.world.entity.animal.SquidMixin_API", - "minecraft.world.entity.animal.TropicalFish_PatternMixin_API", - "minecraft.world.entity.animal.TropicalFishMixin_API", - "minecraft.world.entity.animal.TurtleMixin_API", - "minecraft.world.entity.animal.WaterAnimalMixin_API", - "minecraft.world.entity.animal.WolfMixin_API", - "minecraft.world.entity.animal.WollfVariantMixin_API", + "minecraft.world.entity.animal.bee.BeeMixin_API", + "minecraft.world.entity.animal.feline.CatMixin_API", + "minecraft.world.entity.animal.feline.CatVariantMixin_API", + "minecraft.world.entity.animal.chicken.ChickenMixin_API", + "minecraft.world.entity.animal.chicken.ChickenVariantMixin_API", + "minecraft.world.entity.animal.fish.CodMixin_API", + "minecraft.world.entity.animal.cow.CowVariantMixin_API", + "minecraft.world.entity.animal.dolphin.DolphinMixin_API", + "minecraft.world.entity.animal.fox.Fox_VariantMixin_API", + "minecraft.world.entity.animal.fox.FoxMixin_API", + "minecraft.world.entity.animal.frog.FrogVariantMixin_API", + "minecraft.world.entity.animal.cow.MushroomCow_MushroomTypeMixin_API", + "minecraft.world.entity.animal.cow.MushroomCowMixin_API", + "minecraft.world.entity.animal.feline.OcelotMixin_API", + "minecraft.world.entity.animal.panda.Panda_GeneMixin_API", + "minecraft.world.entity.animal.panda.PandaMixin_API", + "minecraft.world.entity.animal.parrot.Parrot_VariantMixin_API", + "minecraft.world.entity.animal.parrot.ParrotMixin_API", + "minecraft.world.entity.animal.pig.PigMixin_API", + "minecraft.world.entity.animal.pig.PigVariantMixin_API", + "minecraft.world.entity.animal.polarbear.PolarBearMixin_API", + "minecraft.world.entity.animal.fish.PufferfishMixin_API", + "minecraft.world.entity.animal.rabbit.Rabbit_VariantMixin_API", + "minecraft.world.entity.animal.rabbit.RabbitMixin_API", + "minecraft.world.entity.animal.fish.SalmonMixin_API", + "minecraft.world.entity.animal.fish.SalmonVariantMixin_API", + "minecraft.world.entity.animal.parrot.ShoulderRidingEntityMixin_API", + "minecraft.world.entity.animal.fish.TropicalFish_PatternMixin_API", + "minecraft.world.entity.animal.fish.TropicalFishMixin_API", + "minecraft.world.entity.animal.turtle.TurtleMixin_API", + "minecraft.world.entity.animal.fish.WaterAnimalMixin_API", "minecraft.world.entity.animal.allay.AllayMixin_API", "minecraft.world.entity.animal.armadillo.ArmadilloMixin_API", "minecraft.world.entity.animal.axolotl.AxolotlMixin_API", + "minecraft.world.entity.animal.axolotl.AxolotlVariantMixin_API", + "minecraft.world.entity.animal.camel.CamelHuskMixin_API", "minecraft.world.entity.animal.camel.CamelMixin_API", "minecraft.world.entity.animal.frog.FrogMixin_API", "minecraft.world.entity.animal.frog.TadpoleMixin_API", "minecraft.world.entity.animal.goat.GoatMixin_API", - "minecraft.world.entity.animal.horse.AbstractChestedHorseMixin_API", - "minecraft.world.entity.animal.horse.AbstractHorseMixin_API", - "minecraft.world.entity.animal.horse.DonkeyMixin_API", - "minecraft.world.entity.animal.horse.HorseMixin_API", - "minecraft.world.entity.animal.horse.LLama_VariantMixin_API", - "minecraft.world.entity.animal.horse.LlamaMixin_API", - "minecraft.world.entity.animal.horse.MarkingsMixin_API", - "minecraft.world.entity.animal.horse.MuleMixin_API", - "minecraft.world.entity.animal.horse.SkeletonHorseMixin_API", - "minecraft.world.entity.animal.horse.TraderLlamaMixin_API", - "minecraft.world.entity.animal.horse.VariantMixin_API", - "minecraft.world.entity.animal.horse.ZombieHorseMixin_API", + "minecraft.world.entity.animal.golem.AbstractGolemMixin_API", + "minecraft.world.entity.animal.golem.CopperGolemMixin_API", + "minecraft.world.entity.animal.golem.IronGolemMixin_API", + "minecraft.world.entity.animal.golem.SnowGolemMixin_API", + "minecraft.world.entity.animal.happyghast.HappyGhastMixin_API", + "minecraft.world.entity.animal.equine.AbstractChestedHorseMixin_API", + "minecraft.world.entity.animal.equine.AbstractHorseMixin_API", + "minecraft.world.entity.animal.equine.DonkeyMixin_API", + "minecraft.world.entity.animal.equine.HorseMixin_API", + "minecraft.world.entity.animal.equine.LLama_VariantMixin_API", + "minecraft.world.entity.animal.equine.LlamaMixin_API", + "minecraft.world.entity.animal.equine.MarkingsMixin_API", + "minecraft.world.entity.animal.equine.MuleMixin_API", + "minecraft.world.entity.animal.equine.SkeletonHorseMixin_API", + "minecraft.world.entity.animal.equine.TraderLlamaMixin_API", + "minecraft.world.entity.animal.equine.VariantMixin_API", + "minecraft.world.entity.animal.equine.ZombieHorseMixin_API", + "minecraft.world.entity.animal.nautilus.AbstractNautilusMixin_API", + "minecraft.world.entity.animal.nautilus.NautilusMixin_API", + "minecraft.world.entity.animal.nautilus.ZombieNautilusMixin_API", + "minecraft.world.entity.animal.sheep.SheepMixin_API", "minecraft.world.entity.animal.sniffer.SnifferMixin_API", - "minecraft.world.entity.boss.EnderDragonPartMixin_API", + "minecraft.world.entity.animal.squid.GlowSquidMixin_API", + "minecraft.world.entity.animal.squid.SquidMixin_API", + "minecraft.world.entity.animal.wolf.WolfMixin_API", + "minecraft.world.entity.animal.wolf.WolfSoundVariantMixin_API", + "minecraft.world.entity.animal.wolf.WolfVariantMixin_API", + "minecraft.world.entity.boss.enderdragon.EnderDragonPartMixin_API", "minecraft.world.entity.boss.enderdragon.EndCrystalMixin_API", "minecraft.world.entity.boss.enderdragon.EnderDragonMixin_API", "minecraft.world.entity.boss.enderdragon.phases.DragonPhaseInstanceMixin_API", @@ -240,112 +227,113 @@ "minecraft.world.entity.decoration.HangingEntityMixin_API", "minecraft.world.entity.decoration.ItemFrameMixin_API", "minecraft.world.entity.decoration.LeashFenceKnotEntityMixin_API", - "minecraft.world.entity.decoration.PaintingMixin_API", - "minecraft.world.entity.decoration.PaintingVariantMixin_API", + "minecraft.world.entity.decoration.painting.PaintingMixin_API", + "minecraft.world.entity.decoration.painting.PaintingVariantMixin_API", "minecraft.world.entity.item.FallingBlockEntityMixin_API", "minecraft.world.entity.item.ItemEntityMixin_API", "minecraft.world.entity.item.PrimedTntMixin_API", - "minecraft.world.entity.monster.AbstractIllagerMixin_API", - "minecraft.world.entity.monster.AbstractPiglinMixin_API", - "minecraft.world.entity.monster.AbstractSkeletonMixin_API", "minecraft.world.entity.monster.BlazeMixin_API", - "minecraft.world.entity.monster.BoggedMixin_API", - "minecraft.world.entity.monster.BreezeMixin_API", - "minecraft.world.entity.monster.CaveSpiderMixin_API", "minecraft.world.entity.monster.CreeperMixin_API", - "minecraft.world.entity.monster.DrownedMixin_API", "minecraft.world.entity.monster.ElderGuardianMixin_API", "minecraft.world.entity.monster.EnderManMixin_API", "minecraft.world.entity.monster.EndermiteMixin_API", "minecraft.world.entity.monster.EnemyMixin_API", - "minecraft.world.entity.monster.EvokerMixin_API", "minecraft.world.entity.monster.GhastMixin_API", "minecraft.world.entity.monster.GiantMixin_API", "minecraft.world.entity.monster.GuardianMixin_API", - "minecraft.world.entity.monster.HoglinMixin_API", - "minecraft.world.entity.monster.HuskMixin_API", - "minecraft.world.entity.monster.IllusionerMixin_API", - "minecraft.world.entity.monster.MagmaCubeMixin_API", + "minecraft.world.entity.monster.cubemob.AbstractCubeMobMixin_API", + "minecraft.world.entity.monster.cubemob.MagmaCubeMixin_API", "minecraft.world.entity.monster.MonsterMixin_API", "minecraft.world.entity.monster.PatrollingMonsterMixin_API", "minecraft.world.entity.monster.Phantom_AttackPhaseMixin_API", "minecraft.world.entity.monster.PhantomMixin_API", - "minecraft.world.entity.monster.PiglinBruteMixin_API", - "minecraft.world.entity.monster.PiglinMixin_API", - "minecraft.world.entity.monster.PillagerMixin_API", "minecraft.world.entity.monster.RangedAttackMobMixin_API", "minecraft.world.entity.monster.RavagerMixin_API", "minecraft.world.entity.monster.ShulkerMixin_API", "minecraft.world.entity.monster.SilverfishMixin_API", - "minecraft.world.entity.monster.SkeletonMixin_API", - "minecraft.world.entity.monster.SlimeMixin_API", - "minecraft.world.entity.monster.SpellcasterIllager_IllagerSpellMixin_API", - "minecraft.world.entity.monster.SpellcasterIllagerMixin_API", - "minecraft.world.entity.monster.SpiderMixin_API", - "minecraft.world.entity.monster.StrayMixin_API", + "minecraft.world.entity.monster.cubemob.SlimeMixin_API", "minecraft.world.entity.monster.StriderMixin_API", "minecraft.world.entity.monster.VexMixin_API", - "minecraft.world.entity.monster.VindicatorMixin_API", "minecraft.world.entity.monster.WitchMixin_API", - "minecraft.world.entity.monster.WitherSkeletonMixin_API", "minecraft.world.entity.monster.ZoglinMixin_API", - "minecraft.world.entity.monster.ZombieMixin_API", - "minecraft.world.entity.monster.ZombieVillagerMixin_API", - "minecraft.world.entity.monster.ZombifiedPiglinMixin_API", + "minecraft.world.entity.monster.breeze.BreezeMixin_API", + "minecraft.world.entity.monster.hoglin.HoglinMixin_API", + "minecraft.world.entity.monster.illager.AbstractIllagerMixin_API", + "minecraft.world.entity.monster.illager.EvokerMixin_API", + "minecraft.world.entity.monster.illager.IllusionerMixin_API", + "minecraft.world.entity.monster.illager.PillagerMixin_API", + "minecraft.world.entity.monster.illager.SpellcasterIllager_IllagerSpellMixin_API", + "minecraft.world.entity.monster.illager.SpellcasterIllagerMixin_API", + "minecraft.world.entity.monster.illager.VindicatorMixin_API", + "minecraft.world.entity.monster.piglin.AbstractPiglinMixin_API", + "minecraft.world.entity.monster.piglin.PiglinBruteMixin_API", + "minecraft.world.entity.monster.piglin.PiglinMixin_API", + "minecraft.world.entity.monster.skeleton.AbstractSkeletonMixin_API", + "minecraft.world.entity.monster.skeleton.BoggedMixin_API", + "minecraft.world.entity.monster.skeleton.ParchedMixin_API", + "minecraft.world.entity.monster.skeleton.SkeletonMixin_API", + "minecraft.world.entity.monster.skeleton.StrayMixin_API", + "minecraft.world.entity.monster.skeleton.WitherSkeletonMixin_API", + "minecraft.world.entity.monster.spider.CaveSpiderMixin_API", + "minecraft.world.entity.monster.spider.SpiderMixin_API", "minecraft.world.entity.monster.warden.WardenMixin_API", - "minecraft.world.entity.npc.AbstractVillagerMixin_API", - "minecraft.world.entity.npc.VillagerMixin_API", - "minecraft.world.entity.npc.VillagerProfessionMixin_API", - "minecraft.world.entity.npc.VillagerTrades_ItemListingMixin_API", - "minecraft.world.entity.npc.VillagerTypeMixin_API", - "minecraft.world.entity.npc.WanderingTraderMixin_API", + "minecraft.world.entity.monster.zombie.DrownedMixin_API", + "minecraft.world.entity.monster.zombie.HuskMixin_API", + "minecraft.world.entity.monster.zombie.ZombieMixin_API", + "minecraft.world.entity.monster.zombie.ZombieVillagerMixin_API", + "minecraft.world.entity.monster.zombie.ZombifiedPiglinMixin_API", + "minecraft.world.entity.npc.villager.AbstractVillagerMixin_API", + "minecraft.world.entity.npc.villager.VillagerMixin_API", + "minecraft.world.entity.npc.villager.VillagerProfessionMixin_API", + "minecraft.world.item.trading.VillagerTradeMixin_API", + "minecraft.world.entity.npc.villager.VillagerTypeMixin_API", + "minecraft.world.entity.npc.wanderingtrader.WanderingTraderMixin_API", "minecraft.world.entity.player.ChatVisiblityMixin_API", "minecraft.world.entity.player.PlayerMixin_API", - "minecraft.world.entity.projectile.AbstractArrow_PickupMixin_API", - "minecraft.world.entity.projectile.AbstractArrowMixin_API", - "minecraft.world.entity.projectile.AbstractHurtingProjectileMixin_API", - "minecraft.world.entity.projectile.ArrowMixin_API", - "minecraft.world.entity.projectile.DragonFireballMixin_API", "minecraft.world.entity.projectile.EvokerFangsMixin_API", "minecraft.world.entity.projectile.EyeOfEnderMixin_API", - "minecraft.world.entity.projectile.FireballMixin_API", "minecraft.world.entity.projectile.FireworkRocketEntityMixin_API", "minecraft.world.entity.projectile.FishingHookMixin_API", "minecraft.world.entity.projectile.ItemSupplierMixin_API", - "minecraft.world.entity.projectile.LargeFireballMixin_API", "minecraft.world.entity.projectile.LlamaSpitMixin_API", "minecraft.world.entity.projectile.ProjectileMixin_API", "minecraft.world.entity.projectile.ShulkerBulletMixin_API", - "minecraft.world.entity.projectile.SmallFireballMixin_API", - "minecraft.world.entity.projectile.SnowballMixin_API", - "minecraft.world.entity.projectile.SpectralArrowMixin_API", - "minecraft.world.entity.projectile.ThrowableItemProjectileMixin_API", "minecraft.world.entity.projectile.ThrowableProjectileMixin_API", - "minecraft.world.entity.projectile.ThrownEggMixin_API", - "minecraft.world.entity.projectile.ThrownEnderpearlMixin_API", - "minecraft.world.entity.projectile.ThrownExperienceBottleMixin_API", - "minecraft.world.entity.projectile.ThrownPotionMixin_API", - "minecraft.world.entity.projectile.ThrownTridentMixin_API", - "minecraft.world.entity.projectile.WitherSkullMixin_API", - "minecraft.world.entity.projectile.windcharge.AbstractWindChargeMixin_API", - "minecraft.world.entity.projectile.windcharge.BreezeWindChargeMixin_API", - "minecraft.world.entity.projectile.windcharge.WindChargeMixin_API", + "minecraft.world.entity.projectile.arrow.AbstractArrow_PickupMixin_API", + "minecraft.world.entity.projectile.arrow.AbstractArrowMixin_API", + "minecraft.world.entity.projectile.arrow.ArrowMixin_API", + "minecraft.world.entity.projectile.arrow.SpectralArrowMixin_API", + "minecraft.world.entity.projectile.arrow.ThrownTridentMixin_API", + "minecraft.world.entity.projectile.hurtingprojectile.AbstractHurtingProjectileMixin_API", + "minecraft.world.entity.projectile.hurtingprojectile.DragonFireballMixin_API", + "minecraft.world.entity.projectile.hurtingprojectile.FireballMixin_API", + "minecraft.world.entity.projectile.hurtingprojectile.LargeFireballMixin_API", + "minecraft.world.entity.projectile.hurtingprojectile.SmallFireballMixin_API", + "minecraft.world.entity.projectile.hurtingprojectile.WitherSkullMixin_API", + "minecraft.world.entity.projectile.hurtingprojectile.windcharge.AbstractWindChargeMixin_API", + "minecraft.world.entity.projectile.hurtingprojectile.windcharge.BreezeWindChargeMixin_API", + "minecraft.world.entity.projectile.hurtingprojectile.windcharge.WindChargeMixin_API", + "minecraft.world.entity.projectile.throwableitemprojectile.SnowballMixin_API", + "minecraft.world.entity.projectile.throwableitemprojectile.ThrowableItemProjectileMixin_API", + "minecraft.world.entity.projectile.throwableitemprojectile.ThrownEggMixin_API", + "minecraft.world.entity.projectile.throwableitemprojectile.ThrownEnderpearlMixin_API", + "minecraft.world.entity.projectile.throwableitemprojectile.ThrownExperienceBottleMixin_API", + "minecraft.world.entity.projectile.throwableitemprojectile.ThrownPotionMixin_API", "minecraft.world.entity.raid.Raid_RaidStatusMixin_API", "minecraft.world.entity.raid.RaiderMixin_API", "minecraft.world.entity.raid.RaidMixin_API", - "minecraft.world.entity.vehicle.AbstractMinecartContainerMixin_API", - "minecraft.world.entity.vehicle.AbstractMinecartMixin_API", - "minecraft.world.entity.vehicle.Boat_TypeMixin_API", - "minecraft.world.entity.vehicle.BoatMixin_API", - "minecraft.world.entity.vehicle.ChestBoatMixin_API", - "minecraft.world.entity.vehicle.MinecartChestMixin_API", - "minecraft.world.entity.vehicle.MinecartCommandBlockMixin_API", - "minecraft.world.entity.vehicle.MinecartFurnaceMixin_API", - "minecraft.world.entity.vehicle.MinecartHopperMixin_API", - "minecraft.world.entity.vehicle.MinecartMixin_API", - "minecraft.world.entity.vehicle.MinecartSpawnerMixin_API", - "minecraft.world.entity.vehicle.MinecartTNTMixin_API", "minecraft.world.entity.vehicle.VehicleEntityMixin_API", + "minecraft.world.entity.vehicle.boat.AbstractBoatMixin_API", + "minecraft.world.entity.vehicle.boat.AbstractChestBoatMixin_API", + "minecraft.world.entity.vehicle.minecart.AbstractMinecartContainerMixin_API", + "minecraft.world.entity.vehicle.minecart.AbstractMinecartMixin_API", + "minecraft.world.entity.vehicle.minecart.MinecartChestMixin_API", + "minecraft.world.entity.vehicle.minecart.MinecartCommandBlockMixin_API", + "minecraft.world.entity.vehicle.minecart.MinecartFurnaceMixin_API", + "minecraft.world.entity.vehicle.minecart.MinecartHopperMixin_API", + "minecraft.world.entity.vehicle.minecart.MinecartMixin_API", + "minecraft.world.entity.vehicle.minecart.MinecartSpawnerMixin_API", + "minecraft.world.entity.vehicle.minecart.MinecartTNTMixin_API", "minecraft.world.item.ArmorMaterialMixin_API", "minecraft.world.item.DyeColorMixin_API", "minecraft.world.item.FireworkExplosion_ShapeMixin_API", @@ -355,13 +343,20 @@ "minecraft.world.item.ItemStackMixin_API", "minecraft.world.item.JukeboxSongMixin_API", "minecraft.world.item.RarityMixin_API", - "minecraft.world.item.TiersMixin_API", + "minecraft.world.item.ToolMaterialMixin_API", "minecraft.world.item.alchemy.PotionMixin_API", - "minecraft.world.item.armortrim.ArmorTrimMixin_API", - "minecraft.world.item.armortrim.TrimMaterialMixin_API", - "minecraft.world.item.armortrim.TrimPatternMixin_API", + "minecraft.world.item.component.BlocksAttacks_DamageReductionMixin_API", + "minecraft.world.item.component.BlocksAttacks_ItemDamageFunction_API", "minecraft.world.item.component.FireworkExplosionMixin_API", + "minecraft.world.item.component.ItemAttributeModifiers_EntryMixin_API", "minecraft.world.item.component.Tool_RuleMixin_API", + "minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffectMixin_API", + "minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffectMixin_API", + "minecraft.world.item.consume_effects.ConsumeEffect_TypeMixin_API", + "minecraft.world.item.consume_effects.ConsumeEffectMixin_API", + "minecraft.world.item.consume_effects.PlaySoundConsumeEffectMixin_API", + "minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffectMixin_API", + "minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffectMixin_API", "minecraft.world.item.crafting.AbstractCookingRecipeMixin_API", "minecraft.world.item.crafting.CraftingRecipeMixin_API", "minecraft.world.item.crafting.CustomRecipeMixin_API", @@ -375,10 +370,12 @@ "minecraft.world.item.crafting.SmithingTransformRecipeMixin_API", "minecraft.world.item.crafting.StonecutterRecipeMixin_API", "minecraft.world.item.enchantment.EnchantmentMixin_API", + "minecraft.world.item.equipment.trim.ArmorTrimMixin_API", + "minecraft.world.item.equipment.trim.TrimMaterialMixin_API", + "minecraft.world.item.equipment.trim.TrimPatternMixin_API", "minecraft.world.item.trading.MerchantOfferMixin_API", "minecraft.world.level.BlockGetterMixin_API", "minecraft.world.level.EntityGetterMixin_API", - "minecraft.world.level.ExplosionMixin_API", "minecraft.world.level.GameRules_KeyMixin_API", "minecraft.world.level.GameTypeMixin_API", "minecraft.world.level.LevelAccessorMixin_API", @@ -386,9 +383,7 @@ "minecraft.world.level.LevelReaderMixin_API", "minecraft.world.level.LevelWriterMixin_API", "minecraft.world.level.LightLayerMixin_API", - "minecraft.world.level.biome.AmbientAdditionsSettingsMixin_API", - "minecraft.world.level.biome.AmbientMoodSettingsMixin_API", - "minecraft.world.level.biome.AmbientParticleSettingsMixin_API", + "minecraft.world.level.ServerExplosionMixin_API", "minecraft.world.level.biome.Biome_PrecipitationMixin_API", "minecraft.world.level.biome.Biome_TemperatureModifierMixin_API", "minecraft.world.level.biome.BiomeMixin_API", @@ -399,12 +394,13 @@ "minecraft.world.level.biome.Climate_ParameterPointMixin_API", "minecraft.world.level.biome.FixedBiomeSourceMixin_API", "minecraft.world.level.biome.MobSpawnSettings_MobSpawnCostMixin_API", - "minecraft.world.level.biome.MobSpawnSettings_SpawnerDataMixin_API", "minecraft.world.level.biome.MultiNoiseBiomeSourceMixin_API", "minecraft.world.level.biome.TheEndBiomeSourceMixin_API", + "minecraft.world.level.block.CopperGolemStatueBlock_PoseMixin_API", "minecraft.world.level.block.MirrorMixin_API", "minecraft.world.level.block.PortalMixin_API", "minecraft.world.level.block.RotationMixin_API", + "minecraft.world.level.block.WeatheringCopper_WeatherStateMixin_API", "minecraft.world.level.block.entity.AbstractFurnaceBlockEntityMixin_API", "minecraft.world.level.block.entity.BannerBlockEntityMixin_API", "minecraft.world.level.block.entity.BannerPatternLayers_LayerMixin_API", @@ -412,7 +408,6 @@ "minecraft.world.level.block.entity.BarrelBlockEntityMixin_API", "minecraft.world.level.block.entity.BaseContainerBlockEntityMixin_API", "minecraft.world.level.block.entity.BeaconBlockEntityMixin_API", - "minecraft.world.level.block.entity.BedBlockEntityMixin_API", "minecraft.world.level.block.entity.BeehiveBlockEntityMixin_API", "minecraft.world.level.block.entity.BellBlockEntityMixin_API", "minecraft.world.level.block.entity.BlastFurnaceBlockEntityMixin_API", @@ -435,6 +430,7 @@ "minecraft.world.level.block.entity.JigsawBlockEntityMixin_API", "minecraft.world.level.block.entity.JukeboxBlockEntityMixin_API", "minecraft.world.level.block.entity.LecternBlockEntityMixin_API", + "minecraft.world.level.block.entity.PotentSulfurBlockEntityMixin_API", "minecraft.world.level.block.entity.RandomizableContainerBlockEntityMixin_API", "minecraft.world.level.block.entity.SculkSensorBlockEntityMixin_API", "minecraft.world.level.block.entity.ShulkerBoxBlockEntityMixin_API", @@ -460,17 +456,19 @@ "minecraft.world.level.block.state.properties.ChestTypeMixin_API", "minecraft.world.level.block.state.properties.ComparatorModeMixin_API", "minecraft.world.level.block.state.properties.DoorHingeSideMixin_API", - "minecraft.world.level.block.state.properties.DripstoneThicknessMixin_API", + "minecraft.world.level.block.state.properties.SpeleothemThicknessMixin_API", "minecraft.world.level.block.state.properties.FrontAndTopMixin_API", "minecraft.world.level.block.state.properties.HalfMixin_API", "minecraft.world.level.block.state.properties.NoteBlockInstrumentMixin_API", "minecraft.world.level.block.state.properties.PistonTypeMixin_API", + "minecraft.world.level.block.state.properties.PotentSulfurStateMixin_API", "minecraft.world.level.block.state.properties.RailShapeMixin_API", "minecraft.world.level.block.state.properties.RedstoneSideMixin_API", "minecraft.world.level.block.state.properties.SculkSensorPhaseMixin_API", "minecraft.world.level.block.state.properties.SlabTypeMixin_API", "minecraft.world.level.block.state.properties.StairsShapeMixin_API", "minecraft.world.level.block.state.properties.StructureModeMixin_API", + "minecraft.world.level.block.state.properties.TestBlockModeMixin_API", "minecraft.world.level.block.state.properties.TiltMixin_API", "minecraft.world.level.block.state.properties.WallSideMixin_API", "minecraft.world.level.border.WorldBorderMixin_Settings_API", @@ -481,10 +479,10 @@ "minecraft.world.level.chunk.LevelChunkMixin_API", "minecraft.world.level.chunk.ProtoChunkMixin_API", "minecraft.world.level.dimension.DimensionTypeMixin_API", + "minecraft.world.level.dimension.LevelStemMixin_API", "minecraft.world.level.gameevent.PositionSourceMixin_API", "minecraft.world.level.levelgen.DensityFunctionMixin_API", "minecraft.world.level.levelgen.FlatLevelSourceMixin_API", - "minecraft.world.level.levelgen.GenerationStep_CarvingMixin_API", "minecraft.world.level.levelgen.GenerationStep_DecorationMixin_API", "minecraft.world.level.levelgen.Heightmap_TypeMixin_API", "minecraft.world.level.levelgen.NoiseBasedChunkGeneratorMixin_API", @@ -515,7 +513,6 @@ "minecraft.world.level.levelgen.structure.pools.StructureTemplatePoolMixin_API", "minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorListMixin_API", "minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorMixin_API", - "minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorTypeMixin_API", "minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateMixin_API", "minecraft.world.level.material.FluidMixin_API", "minecraft.world.level.material.FluidStateMixin_API", @@ -535,6 +532,7 @@ "minecraft.world.ticks.ScheduledTickMixin_API", "minecraft.world.ticks.TickPriorityMixin_API", "service.permission.SubjectMixin_API", + "tag.TaggableMixin", "world.LocatableBlockMixin_API" ], "client": [ diff --git a/src/mixins/resources/mixins.sponge.core.json b/src/mixins/resources/mixins.sponge.core.json index d6c2d4a431f..7d5d01d8e1e 100644 --- a/src/mixins/resources/mixins.sponge.core.json +++ b/src/mixins/resources/mixins.sponge.core.json @@ -4,14 +4,14 @@ "package": "org.spongepowered.common.mixin.core", "mixinPriority": 1101, "mixins": [ + "HolderLookup_ProviderMixin", "advancements.AdvancementMixin", "advancements.AdvancementProgressMixin", "advancements.CriteriaTriggersMixin", "advancements.CriterionMixin", "advancements.CriterionProgressMixin", - "advancements.CriterionTrigger_ListenerMixin", "advancements.DisplayInfoMixin", - "advancements.TreeNodePositionMixin", + "advancements.SimpleCriterionTriggerMixin", "adventure.KeyMixin", "adventure.bossbar.HackyBossBarPlatformBridgeMixin", "adventure.chat.SignedMessageMixin", @@ -20,10 +20,10 @@ "block.BedBlockMixin", "block.BlockMixin", "block.DyeColorBlockPropertiesMixin", + "commands.CommandFunctionMixin", "commands.CommandsMixin", "commands.CommandSourceMixin", "commands.CommandSourceStackMixin", - "commands.arguments.ComponentArgumentMixin", "commands.arguments.DimensionArgumentMixin", "commands.arguments.selector.EntitySelectorParserMixin", "commands.execution.tasks.ExecuteCommandMixin", @@ -52,31 +52,29 @@ "network.syncher.EntityDataAccessorMixin", "network.syncher.SynchedEntityData_BuilderMixin", "network.syncher.SynchedEntityDataMixin", - "registries.BuiltInRegistriesMixin", - "resources.RegistryDataLoaderMixin", - "server.MainMixin", + "resources.RegistryLoadTaskMixin", "server.MinecraftServerMixin", "server.PlayerAdvancementsMixin", - "server.ReloadableServerRegistriesMixin", - "server.ServerFunctionLibraryMixin", + "server.ReloadableServerResourcesMixin", + "server.ServerAdvancementManagerMixin", "server.ServerFunctionManagerMixin", "server.ServerScoreboardMixin", "server.WorldLoaderMixin", "server.bossevents.CustomBossEventMixin", "server.commands.BossBarCommandsMixin", "server.commands.DifficultyCommandMixin", - "server.commands.GameRuleCommandMixin", "server.commands.KickCommandMixin", "server.commands.SaveAllCommandMixin", + "server.commands.SpreadPlayersCommandMixin", "server.commands.TeleportCommandMixin", "server.commands.WeatherCommandMixin", - "server.commands.WorldBorderCommandMixin", "server.dedicated.DedicatedPlayerListMixin", "server.level.ChunkHolderMixin", "server.level.ChunkMap_TrackedEntityMixin", "server.level.ChunkMapMixin", "server.level.DistanceManagerMixin", "server.level.GenerationChunkHolderMixin", + "server.level.PlayerSpawnFinderMixin", "server.level.ServerBossEventMixin", "server.level.ServerChunkCacheMixin", "server.level.ServerEntityMixin", @@ -84,17 +82,17 @@ "server.level.ServerPlayer_ContainerListenerMixin", "server.level.ServerPlayerGameModeMixin", "server.level.ServerPlayerMixin", - "server.level.ServerPlayerMixin_Attack_impl", "server.level.ServerPlayerMixin_HealthScale", "server.level.TicketMixin", + "server.level.TicketStorageMixin", "server.network.LegacyQueryHandlerMixin", "server.network.ServerCommonPacketListenerImplMixin", "server.network.ServerConfigurationPacketListenerImplMixin", - "server.network.ServerGamePacketListenerImpl_1Mixin", "server.network.ServerGamePacketListenerImplMixin", "server.network.ServerHandshakePacketListenerImplMixin", "server.network.ServerLoginPacketListenerImplMixin", "server.network.ServerStatusPacketListenerImplMixin", + "server.network.config.PrepareSpawnTask_ReadyMixin", "server.packs.resoruces.MultiPackResourceManagerMixin", "server.players.BanListEntryMixin", "server.players.GameProfileCache_GameProfileInfoMixin", @@ -108,9 +106,9 @@ "server.rcon.thread.QueryThreadGs4Mixin", "server.rcon.thread.RconClientMixin", "service.permission.SubjectMixin", + "tags.TagKeyMixin", "tags.TagLoader_EntryWithSourceMixin", "tags.TagLoaderMixin", - "tags.TagManagerMixin", "util.ClassInstanceMultiMapMixin", "util.datafix.schemas.V1125Mixin", "world.BaseCommandBlockMixin", @@ -119,6 +117,7 @@ "world.damagesource.DamageSourcesMixin", "world.entity.ActiveChunkReferentMixin", "world.entity.AgableMobMixin", + "world.entity.AvatarMixin", "world.entity.EntityMaxAirMixin", "world.entity.EntityMixin", "world.entity.EntitySelectorMixin", @@ -128,9 +127,9 @@ "world.entity.LeashableMixin", "world.entity.LightningBoltMixin", "world.entity.LivingEntityMixin", - "world.entity.LivingEntityMixin_Attack_Impl", + "world.entity.LivingEntityMixin_Damage", "world.entity.MobMixin", - "world.entity.MobMixin_Attack_Impl", + "world.entity.MobMixin_Attack", "world.entity.PortalProcessorMixin", "world.entity.ai.goal.BreakDoorGoalMixin", "world.entity.ai.goal.BreedGoalMixin", @@ -142,17 +141,19 @@ "world.entity.ai.sensing.NearestLivingEntitySensorMixin", "world.entity.ai.sensing.SensorMixin", "world.entity.animal.AnimalMixin", - "world.entity.animal.Cat_CatRelaxOnOwnerGoalMixin", - "world.entity.animal.OcelotMixin", - "world.entity.animal.ParrotMixin", - "world.entity.animal.Rabbit_RaidGardenGoalMixin", - "world.entity.animal.WolfMixin", - "world.entity.animal.horse.AbstractHorseMixin", + "world.entity.animal.feline.Cat_CatRelaxOnOwnerGoalMixin", + "world.entity.animal.feline.OcelotMixin", + "world.entity.animal.parrot.ParrotMixin", + "world.entity.animal.fish.PufferfishMixin", + "world.entity.animal.rabbit.Rabbit_RaidGardenGoalMixin", + "world.entity.animal.equine.AbstractHorseMixin", + "world.entity.animal.wolf.WolfMixin", "world.entity.boss.enderdragon.EndCrystalMixin", "world.entity.decoration.ArmorStandMixin", "world.entity.decoration.BlockAttachedEntityMixin", "world.entity.decoration.HangingEntityMixin", "world.entity.decoration.ItemFrameMixin", + "world.entity.decoration.MannequinMixin", "world.entity.item.ItemEntityMixin", "world.entity.item.PrimedTntMixin", "world.entity.monster.Blaze_BlazeAttackGoalMixin", @@ -166,40 +167,41 @@ "world.entity.monster.ShulkerMixin", "world.entity.monster.Silverfish_SilverfishMergeWithStoneGoalMixin", "world.entity.monster.Silverfish_SilverfishWakeUpFriendsGoalMixin", - "world.entity.monster.ZombifiedPiglinMixin", - "world.entity.npc.AbstractVillagerMixin", - "world.entity.npc.VillagerMixin", - "world.entity.npc.WanderingTraderMixin", + "world.entity.monster.zombie.ZombifiedPiglinMixin", + "world.entity.npc.villager.AbstractVillagerMixin", + "world.entity.npc.villager.VillagerMixin", + "world.entity.npc.wanderingtrader.WanderingTraderMixin", "world.entity.player.PlayerMixin", - "world.entity.player.PlayerMixin_Attack_Impl", - "world.entity.projectile.AbstractArrowMixin", - "world.entity.projectile.AbstractHurtingProjectileMixin", + "world.entity.player.PlayerMixin_Attack", + "world.entity.projectile.arrow.AbstractArrowMixin", + "world.entity.projectile.hurtingprojectile.AbstractHurtingProjectileMixin", "world.entity.projectile.EyeOfEnderMixin", "world.entity.projectile.FireworkRocketEntityMixin", "world.entity.projectile.FishingHookMixin", - "world.entity.projectile.LargeFireballMixin", + "world.entity.projectile.hurtingprojectile.LargeFireballMixin", "world.entity.projectile.LlamaSpitMixin", "world.entity.projectile.ProjectileMixin", "world.entity.projectile.ProjectileUtilMixin", "world.entity.projectile.ShulkerBulletMixin", - "world.entity.projectile.SmallFireballMixin", - "world.entity.projectile.SnowballMixin", + "world.entity.projectile.hurtingprojectile.SmallFireballMixin", + "world.entity.projectile.throwableitemprojectile.SnowballMixin", "world.entity.projectile.ThrowableProjectileMixin", - "world.entity.projectile.ThrownEggMixin", - "world.entity.projectile.ThrownEnderpearlMixin", - "world.entity.projectile.ThrownTridentMixin", - "world.entity.projectile.WitherSkullMixin", - "world.entity.vehicle.AbstractMinecartContainerMixin", - "world.entity.vehicle.AbstractMinecartMixin", - "world.entity.vehicle.BoatMixin", - "world.entity.vehicle.MinecartCommandBlock_MinecartCommandBaseMixin", - "world.entity.vehicle.MinecartCommandBlockMixin", - "world.entity.vehicle.MinecartFurnaceMixin", - "world.entity.vehicle.MinecartTNTMixin", + "world.entity.projectile.throwableitemprojectile.ThrownEggMixin", + "world.entity.projectile.throwableitemprojectile.ThrownEnderpearlMixin", + "world.entity.projectile.arrow.ThrownTridentMixin", + "world.entity.projectile.hurtingprojectile.WitherSkullMixin", + "world.entity.vehicle.boat.AbstractBoatMixin", + "world.entity.vehicle.minecart.AbstractMinecartContainerMixin", + "world.entity.vehicle.minecart.AbstractMinecartMixin", + "world.entity.vehicle.minecart.MinecartCommandBlock_MinecartCommandBaseMixin", + "world.entity.vehicle.minecart.MinecartCommandBlockMixin", + "world.entity.vehicle.minecart.MinecartFurnaceMixin", + "world.entity.vehicle.minecart.MinecartTNTMixin", + "world.entity.vehicle.minecart.OldMinecartBehaviourMixin", "world.entity.vehicle.VehicleEntityMixin", "world.food.FoodDataMixin", "world.inventory.StonecutterMenuMixin", - "world.item.ChorusFruitItemMixin", + "world.item.ConsumableMixin", "world.item.EnderEyeItemMixin", "world.item.FireworkRocketItemMixin", "world.item.FishingRodItemMixin", @@ -210,28 +212,24 @@ "world.item.crafting.AbstractCookingRecipeMixin", "world.item.crafting.IngredientMixin", "world.item.crafting.RecipeManagerMixin", - "world.item.crafting.ShapedRecipe_SerializerMixin", "world.item.crafting.ShapedRecipeMixin", - "world.item.crafting.ShapelessRecipe_SerializerMixin", "world.item.crafting.ShapelessRecipeMixin", - "world.item.crafting.SimpleCookingSerializerMixin", - "world.item.crafting.SimpleCraftingRecipeSerializerMixin", - "world.item.crafting.SingleItemRecipe_SerializerMixin", "world.item.crafting.SingleItemRecipeMixin", - "world.item.crafting.SmithingTransformRecipe_SerializerMixin", "world.item.crafting.SmithingTransformRecipeMixin", - "world.item.enchantment.EnchantmentMixin", + "world.item.enchantment.EnchantmentMixin_Attack", + "util.filefix.FileFixerUpperMixin", "world.level.BaseSpawnerMixin", "world.level.BlockGetterMixin", - "world.level.ExplosionMixin", + "world.level.Explosion_BlockInteractionMixin", "world.level.LevelMixin", "world.level.NaturalSpawner_SpawnStateMixin", "world.level.NaturalSpawnerMixin", - "world.level.SavedDataMixin", + "world.level.ServerExplosionMixin", "world.level.biome.BiomeSourceMixin", "world.level.block.BaseFireBlockMixin", "world.level.block.CactusBlockMixin", "world.level.block.CampfireBlockMixin", + "world.level.block.ColorCollectionMixin", "world.level.block.CarvedPumpkinBlockMixin", "world.level.block.CommandBlockMixin", "world.level.block.EndPortalBlockMixin", @@ -244,9 +242,11 @@ "world.level.block.NoteBlockMixin", "world.level.block.PointedDripstoneBlockMixin", "world.level.block.SweetBerryBushBlockMixin", + "world.level.block.TntBlockMixin", "world.level.block.entity.AbstractFurnaceBlockEntityMixin", "world.level.block.entity.BannerBlockEntityMixin", "world.level.block.entity.BaseContainerBlockEntityMixin", + "world.level.block.entity.BeaconBlockEntityMixin", "world.level.block.entity.BellBLockEntityMixin", "world.level.block.entity.BlockEntityMixin", "world.level.block.entity.BrewingStandBlockEntityMixin", @@ -264,10 +264,10 @@ "world.level.block.state.BlockStateMixin", "world.level.border.WorldBorderMixin", "world.level.chunk.ChunkGeneratorMixin", - "world.level.chunk.ChunkSerializerMixin", "world.level.chunk.ChunkSourceMixin", "world.level.chunk.ChunkStatusTasksMixin", "world.level.chunk.LevelChunkMixin", + "world.level.chunk.SerializableChunkDataMixin", "world.level.chunk.storage.EntityStorageMixin", "world.level.chunk.storage.IOWorkerMixin", "world.level.chunk.storage.RegionFileMixin", @@ -277,10 +277,9 @@ "world.level.levelgen.WorldDimensionsMixin", "world.level.levelgen.flat.FlatLayerInfoMixin", "world.level.levelgen.flat.FlatLevelGeneratorSettingsMixin", - "world.level.levelgen.structure.LegacyStructureDataHandlerMixin", "world.level.saveddata.MapIdTrackerMixin", "world.level.storage.DerivedLevelDataMixin", - "world.level.storage.DimensionDataStorageMixin", + "world.level.storage.SavedDataStorageMixin", "world.level.storage.LevelDataMixin", "world.level.storage.LevelStorageSource_LevelStorageAccessMixin", "world.level.storage.PlayerDataStorageMixin", @@ -296,6 +295,7 @@ ], "client": [ "client.MinecraftMixin", + "client.gui.screens.worldselection.WorldOpenFlowsMixin", "client.multiplayer.ClientCommonPacketListenerImplMixin", "client.multiplayer.ClientHandshakePacketListenerImplMixin", "client.multiplayer.ClientLevelMixin", diff --git a/src/mixins/resources/mixins.sponge.entityactivation.json b/src/mixins/resources/mixins.sponge.entityactivation.json index 79fd4a3428c..7e0303e48d3 100644 --- a/src/mixins/resources/mixins.sponge.entityactivation.json +++ b/src/mixins/resources/mixins.sponge.entityactivation.json @@ -8,7 +8,7 @@ "entity.LivingEntityMixin_Activation", "entity.item.FireworkRocketEntityMixin_EntityActivation", "entity.item.ItemEntityMixin_EntityActivation", - "entity.projectile.AbstractArrowEntityMixin_EntityActivation", + "entity.projectile.arrow.AbstractArrowEntityMixin_EntityActivation", "world.server.ServerLevelMixin_EntityActivation" ] } diff --git a/src/mixins/resources/mixins.sponge.inventory.json b/src/mixins/resources/mixins.sponge.inventory.json index 5c1dbaafcc0..94494229924 100644 --- a/src/mixins/resources/mixins.sponge.inventory.json +++ b/src/mixins/resources/mixins.sponge.inventory.json @@ -18,15 +18,16 @@ "api.world.CompoundContainerMixin_Carrier_Inventory_API", "api.world.ContainerMixin_Inventory_API", "api.world.entity.EquipmentSlot_TypeMixin_Inventory_API", + "api.world.entity.EquipmentSlotGroupMixin_Inventory_API", "api.world.entity.EquipmentSlotMixin_Inventory_API", - "api.world.entity.animal.horse.AbstractHorseMixin_Carrier_Inventory_API", - "api.world.entity.monster.PillagerMixin_Carrier_Inventory_API", - "api.world.entity.npc.AbstractVillagerMixin_Carrier_Inventory_API", + "api.world.entity.animal.equine.AbstractHorseMixin_Carrier_Inventory_API", + "api.world.entity.monster.illager.PillagerMixin_Carrier_Inventory_API", + "api.world.entity.npc.villager.AbstractVillagerMixin_Carrier_Inventory_API", "api.world.entity.player.InventoryMixin_Carried_Inventory_API", "api.world.entity.player.InventoryMixin_Inventory_API", "api.world.entity.player.PlayerMixin_Inventory_API", - "api.world.entity.vehicle.AbstractMinecartContainerMixin_Carried_Inventory_API", - "api.world.entity.vehicle.AbstractMinecartContainerMixin_Carrier_Inventory_API", + "api.world.entity.vehicle.minecart.AbstractMinecartContainerMixin_Carried_Inventory_API", + "api.world.entity.vehicle.minecart.AbstractMinecartContainerMixin_Carrier_Inventory_API", "api.world.inventory.AnvilMenuMixin_BlockCarrier_Inventory_API", "api.world.inventory.ArmorSlot_Inventory_API", "api.world.inventory.BrewingStandMenu_PotionSlotMixin_Inventory_API", @@ -87,6 +88,7 @@ "impl.world.inventory.AbstractContainerMenuMixin_Bridge_Inventory", "impl.world.inventory.AbstractContainerMenuMixin_Fabric_Inventory", "impl.world.inventory.AbstractContainerMenuMixin_Menu_Inventory", + "impl.world.inventory.AbstractCraftingMenuMixin_TrackedMenuBridge_Inventory", "impl.world.inventory.AbstractFurnaceMenuMixin_TrackedMenuBridge_Inventory", "impl.world.inventory.BeaconMenuMixin_TrackedMenuBridge_Inventory", "impl.world.inventory.BrewingStandMenuMixin_TrackedMenuBridge_Inventory", diff --git a/src/mixins/resources/mixins.sponge.tracker.json b/src/mixins/resources/mixins.sponge.tracker.json index 1ddb1474db7..816d2e8fc13 100644 --- a/src/mixins/resources/mixins.sponge.tracker.json +++ b/src/mixins/resources/mixins.sponge.tracker.json @@ -1,56 +1,57 @@ { - "required": true, - "parent": "mixins.sponge.parent.json", - "package": "org.spongepowered.common.mixin.tracker", - "mixinPriority": 1102, - "mixins": [ - "CreatorTrackedMixin_Tracker", - "CreatorTrackedPlayerMixin_Tracker", - "TrackableTypeMixin", - "event.TraitMixin_Tracked_Inventory", - "network.protocol.PacketUtilsMixin_Tracker", - "server.MinecraftServerMixin_Tracker", - "server.TickTaskMixin_Tracker", - "server.dedicated.DedicatedServerMixin_Tracker", - "server.level.ChunkMapMixin_Tracker", - "server.level.ChunkStatusTasksMixin_Tracker", - "server.level.ServerChunkCacheMixin_Tracker", - "server.level.ServerLevelMixin_Tracker", - "server.level.ServerPlayerGameModeMixin_Tracker", - "server.level.ServerPlayerMixin_Tracker", - "server.network.ServerGamePacketListenerImplMixin_Tracker", - "server.players.PlayerListMixin_Tracker", - "util.thread.BlockableEventLoopMixin_Tracker", - "world.entity.EntityMixin_Tracker", - "world.entity.EntityTypeMixin_Tracker", - "world.entity.LivingEntityMixin_Tracker", - "world.entity.animal.AnimalMixin_Tracker", - "world.entity.item.FallingBlockEntityMixin_Tracker", - "world.entity.item.PrimedTntMixin_Tracker", - "world.entity.player.InventoryMixin_Tracker", - "world.entity.player.PlayerMixin_Tracker", - "world.item.BoneMealItemMixin_Tracker", - "world.item.ItemStackMixin_Tracker", - "world.level.BlockEventDataMixin_Tracker", - "world.level.ExplosionMixin_Tracker", - "world.level.LevelAccessorMixin_Tracker", - "world.level.LevelHeightAccessorMixin_Tracker", - "world.level.LevelMixin_Tracker", - "world.level.block.BlockMixin_Tracker", - "world.level.block.DispenserBlockMixin_Tracker", - "world.level.block.LeavesBlockMixin_Tracker", - "world.level.block.SaplingBlockMixin_Tracker", - "world.level.block.entity.BlockEntityMixin_Tracker", - "world.level.block.entity.BlockEntityTypeMixin_Tracker", - "world.level.chunk.ChunkAccessMixin_Tracker", - "world.level.chunk.LevelChunk_BoundTickingBlockEntityMixin_Tracker", - "world.level.chunk.LevelChunkMixin_Tracker", - "world.level.chunk.storage.ChunkSerializerMixin_Tracker", - "world.level.dimension.end.EndDragonFightMixin_Tracker", - "world.level.redstone.NeighborUpdaterMixin_Tracker", - "world.ticks.LevelTicksMixin_Tracker" - ], - "injectors": { - "maxShiftBy": 3 - } + "required": true, + "parent": "mixins.sponge.parent.json", + "package": "org.spongepowered.common.mixin.tracker", + "mixinPriority": 1102, + "mixins": [ + "CreatorTrackedMixin_Tracker", + "CreatorTrackedPlayerMixin_Tracker", + "TrackableTypeMixin", + "event.TraitMixin_Tracked_Inventory", + "network.PacketProcessor_ListenerAndPacketMixin_Tracker", + "server.MinecraftServerMixin_Tracker", + "server.TickTaskMixin_Tracker", + "server.dedicated.DedicatedServerMixin_Tracker", + "server.level.ChunkMapMixin_Tracker", + "server.level.ChunkStatusTasksMixin_Tracker", + "server.level.ServerChunkCacheMixin_Tracker", + "server.level.ServerLevelMixin_Tracker", + "server.level.ServerPlayerGameModeMixin_Tracker", + "server.level.ServerPlayerMixin_Tracker", + "server.network.ServerGamePacketListenerImplMixin_Tracker", + "server.players.PlayerListMixin_Tracker", + "util.thread.BlockableEventLoopMixin_Tracker", + "world.entity.EntityMixin_Tracker", + "world.entity.EntityTypeMixin_Tracker", + "world.entity.EntityTypesMixin_Tracker", + "world.entity.LivingEntityMixin_Tracker", + "world.entity.animal.AnimalMixin_Tracker", + "world.entity.item.FallingBlockEntityMixin_Tracker", + "world.entity.item.PrimedTntMixin_Tracker", + "world.entity.player.InventoryMixin_Tracker", + "world.entity.player.PlayerMixin_Tracker", + "world.item.BoneMealItemMixin_Tracker", + "world.item.ItemStackMixin_Tracker", + "world.level.BlockEventDataMixin_Tracker", + "world.level.ServerExplosionMixin_Tracker", + "world.level.LevelHeightAccessorMixin_Tracker", + "world.level.LevelMixin_Tracker", + "world.level.block.BlockMixin_Tracker", + "world.level.block.DispenserBlockMixin_Tracker", + "world.level.block.LeavesBlockMixin_Tracker", + "world.level.block.SaplingBlockMixin_Tracker", + "world.level.block.entity.BlockEntityMixin_Tracker", + "world.level.block.entity.BlockEntityTypeMixin_Tracker", + "world.level.block.entity.BlockEntityTypesMixin_Tracker", + "world.level.chunk.ChunkAccessMixin_Tracker", + "world.level.chunk.LevelChunk_BoundTickingBlockEntityMixin_Tracker", + "world.level.chunk.LevelChunkMixin_Tracker", + "world.level.chunk.storage.SerializableChunkDataMixin_Tracker", + "world.level.dimension.end.EndDragonFightMixin_Tracker", + "world.level.redstone.NeighborUpdaterMixin_Tracker", + "world.ticks.LevelTicksMixin_Tracker" + ], + "injectors": { + "maxShiftBy": 3 + } } \ No newline at end of file diff --git a/src/test/java/org/spongepowered/common/data/EntityDataTest.java b/src/test/java/org/spongepowered/common/data/EntityDataTest.java index ff50a9b92c2..28bfaa4cfef 100644 --- a/src/test/java/org/spongepowered/common/data/EntityDataTest.java +++ b/src/test/java/org/spongepowered/common/data/EntityDataTest.java @@ -34,7 +34,6 @@ import org.spongepowered.api.block.BlockTypes; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.ArtTypes; -import org.spongepowered.api.data.type.BoatTypes; import org.spongepowered.api.data.type.BodyParts; import org.spongepowered.api.data.type.CatTypes; import org.spongepowered.api.data.type.DyeColors; @@ -58,6 +57,7 @@ import org.spongepowered.api.entity.EntityArchetype; import org.spongepowered.api.entity.EntitySnapshot; import org.spongepowered.api.entity.EntityTypes; +import org.spongepowered.api.entity.Mannequin; import org.spongepowered.api.entity.living.ArmorStand; import org.spongepowered.api.entity.living.Living; import org.spongepowered.api.entity.living.animal.Sheep; @@ -185,8 +185,8 @@ public void testZombifiedPiglin() { final Entity zombifiedPiglin = this.location.createEntity(EntityTypes.ZOMBIFIED_PIGLIN.get()); final Entity pig = this.location.createEntity(EntityTypes.PIG.get()); - DataTest.checkGetData(zombifiedPiglin, Keys.ANGER_LEVEL, 0); - DataTest.checkOfferData(zombifiedPiglin, Keys.ANGER_LEVEL, 10); + DataTest.checkGetData(zombifiedPiglin, Keys.ANGER_LEVEL, 0l); + DataTest.checkOfferData(zombifiedPiglin, Keys.ANGER_LEVEL, 10l); DataTest.checkOfferData(zombifiedPiglin, Keys.TARGET_ENTITY, pig); } @@ -295,7 +295,6 @@ public void testFallingBlock() { DataTest.checkOfferData(fallingBlock, Keys.MAX_FALL_DAMAGE, 50.0); } - @Disabled @Test public void testArmorStand() { final ArmorStand armorStand = this.location.createEntity(EntityTypes.ARMOR_STAND.get()); @@ -307,9 +306,6 @@ public void testArmorStand() { armorStand.equip(EquipmentTypes.MAINHAND.get(), ItemStack.of(ItemTypes.DIAMOND)); armorStand.equip(EquipmentTypes.OFFHAND.get(), ItemStack.of(ItemTypes.DIAMOND)); - DataTest.checkOfferData(armorStand, Keys.BODY_ROTATIONS, Map.of(BodyParts.CHEST.get(), Vector3d.RIGHT)); - DataTest.checkOfferData(armorStand, Keys.CHEST_ROTATION, Vector3d.from(0, 90, 0)); - DataTest.checkOfferData(armorStand, Keys.HAS_ARMS, false); DataTest.checkOfferData(armorStand, Keys.HAS_ARMS, true); @@ -319,14 +315,36 @@ public void testArmorStand() { DataTest.checkOfferData(armorStand, Keys.HAS_MARKER, true); DataTest.checkOfferData(armorStand, Keys.HAS_MARKER, false); - DataTest.checkOfferData(armorStand, Keys.HEAD_ROTATION, Vector3d.from(0, 90, 0)); - DataTest.checkOfferData(armorStand, Keys.LEFT_ARM_ROTATION, Vector3d.from(0, -90, -90)); - DataTest.checkOfferData(armorStand, Keys.LEFT_LEG_ROTATION, Vector3d.from(0, -90, -45)); - DataTest.checkOfferData(armorStand, Keys.RIGHT_ARM_ROTATION, Vector3d.from(0, 90, 90)); - DataTest.checkOfferData(armorStand, Keys.RIGHT_LEG_ROTATION, Vector3d.from(0, 90, 45)); - DataTest.checkOfferData(armorStand, Keys.IS_SMALL, true); DataTest.checkOfferData(armorStand, Keys.IS_SMALL, false); + + DataTest.checkOfferData(armorStand, Keys.HEAD_ROTATION, new Vector3d(0, 90, 0)); + DataTest.checkOfferData(armorStand, Keys.CHEST_ROTATION, new Vector3d(0, 90, 0)); + DataTest.checkOfferData(armorStand, Keys.LEFT_ARM_ROTATION, new Vector3d(0, -90, -90)); + DataTest.checkOfferData(armorStand, Keys.RIGHT_ARM_ROTATION, new Vector3d(0, 90, 90)); + DataTest.checkOfferData(armorStand, Keys.LEFT_LEG_ROTATION, new Vector3d(0, -90, -45)); + DataTest.checkOfferData(armorStand, Keys.RIGHT_LEG_ROTATION, new Vector3d(0, 90, 45)); + + DataTest.checkOfferData(armorStand, Keys.BODY_ROTATIONS, Map.of( + BodyParts.HEAD.get(), new Vector3d(0, -90, 0), + BodyParts.CHEST.get(), new Vector3d(0, -90, 0), + BodyParts.LEFT_ARM.get(), new Vector3d(0, 90, 90), + BodyParts.RIGHT_ARM.get(), new Vector3d(0, -90, -90), + BodyParts.LEFT_LEG.get(), new Vector3d(0, 90, 45), + BodyParts.RIGHT_LEG.get(), new Vector3d(0, -90, -45) + )); + } + + @Test + public void testMannequin() { + final Mannequin mannequin = this.location.createEntity(EntityTypes.MANNEQUIN.get()); + + mannequin.equip(EquipmentTypes.CHEST.get(), ItemStack.of(ItemTypes.LEATHER_CHESTPLATE)); + mannequin.equip(EquipmentTypes.FEET.get(), ItemStack.of(ItemTypes.CHAINMAIL_BOOTS)); + mannequin.equip(EquipmentTypes.HEAD.get(), ItemStack.of(ItemTypes.GOLDEN_HELMET)); + mannequin.equip(EquipmentTypes.LEGS.get(), ItemStack.of(ItemTypes.DIAMOND_LEGGINGS)); + mannequin.equip(EquipmentTypes.MAINHAND.get(), ItemStack.of(ItemTypes.DIAMOND)); + mannequin.equip(EquipmentTypes.OFFHAND.get(), ItemStack.of(ItemTypes.DIAMOND)); } @Test @@ -342,7 +360,7 @@ public void testCreeper() { @Test public void testBoat() { - final Entity boat = this.location.createEntity(EntityTypes.BOAT.get()); + final Entity boat = this.location.createEntity(EntityTypes.OAK_BOAT.get()); DataTest.checkOfferData(boat, Keys.CAN_MOVE_ON_LAND, true); DataTest.checkOfferData(boat, Keys.CAN_MOVE_ON_LAND, false); @@ -352,8 +370,6 @@ public void testBoat() { DataTest.checkOfferData(boat, Keys.OCCUPIED_DECELERATION, 2.0); DataTest.checkOfferData(boat, Keys.UNOCCUPIED_DECELERATION, 2.0); - - DataTest.checkOfferData(boat, Keys.BOAT_TYPE, BoatTypes.ACACIA.get()); } @Test @@ -418,7 +434,7 @@ public void testTNT() { DataTest.checkGetData(tnt, Keys.DETONATOR, null); DataTest.checkOfferData(tnt, Keys.DETONATOR, evilChicken); - DataTest.checkOfferData(tnt, Keys.EXPLOSION_RADIUS, 1); + DataTest.checkOfferData(tnt, Keys.EXPLOSION_RADIUS, 1f); DataTest.checkOfferData(tnt, Keys.FUSE_DURATION, Ticks.of(10)); DataTest.checkGetData(tnt, Keys.IS_PRIMED, true); } @@ -685,7 +701,7 @@ public void testItemStackSnapshot() { final ItemStackSnapshot itemStack = ItemStack.of(ItemTypes.GOLDEN_APPLE).asImmutable(); final Entity itemFrame = this.location.createEntity(EntityTypes.ITEM_FRAME.get()); final Entity item = this.location.createEntity(EntityTypes.ITEM.get()); - final Entity potion = this.location.createEntity(EntityTypes.POTION.get()); + final Entity potion = this.location.createEntity(EntityTypes.SPLASH_POTION.get()); DataTest.checkOfferData(item, Keys.ITEM_STACK_SNAPSHOT, itemStack); DataTest.checkOfferData(itemFrame, Keys.ITEM_STACK_SNAPSHOT, itemStack); @@ -722,6 +738,7 @@ public void testMooshroom() { DataTest.checkOfferData(mooshroom, Keys.MOOSHROOM_TYPE, MooshroomTypes.RED.get()); } + @Disabled @Test public void testParrot() { final Entity parrot = this.location.createEntity(EntityTypes.PARROT.get()); diff --git a/src/test/java/org/spongepowered/common/data/ItemStackDataTest.java b/src/test/java/org/spongepowered/common/data/ItemStackDataTest.java index a16d6ca2d5e..6c81b99b093 100644 --- a/src/test/java/org/spongepowered/common/data/ItemStackDataTest.java +++ b/src/test/java/org/spongepowered/common/data/ItemStackDataTest.java @@ -29,13 +29,19 @@ import net.kyori.adventure.text.format.TextDecoration; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.block.BlockTypes; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.ArmorMaterials; import org.spongepowered.api.data.type.DyeColors; import org.spongepowered.api.data.type.ItemTiers; import org.spongepowered.api.data.type.NotePitches; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; +import org.spongepowered.api.effect.sound.SoundTypes; import org.spongepowered.api.effect.sound.music.MusicDiscs; +import org.spongepowered.api.entity.EntityTypes; +import org.spongepowered.api.event.cause.entity.damage.DamageTypes; import org.spongepowered.api.fluid.FluidStackSnapshot; import org.spongepowered.api.fluid.FluidTypes; import org.spongepowered.api.item.ItemRarities; @@ -43,11 +49,13 @@ import org.spongepowered.api.item.enchantment.Enchantment; import org.spongepowered.api.item.enchantment.EnchantmentTypes; import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.equipment.EquipmentTypes; import org.spongepowered.api.item.potion.PotionTypes; import org.spongepowered.api.item.recipe.smithing.ArmorTrim; import org.spongepowered.api.item.recipe.smithing.TrimMaterials; import org.spongepowered.api.item.recipe.smithing.TrimPatterns; import org.spongepowered.api.util.Color; +import org.spongepowered.api.util.Ticks; import java.util.Arrays; import java.util.HashSet; @@ -56,6 +64,54 @@ public final class ItemStackDataTest { + @Test + public void testWeapon() { + final ItemStack weapon = ItemStack.of(ItemTypes.DIAMOND_SWORD); + DataTest.checkOfferData(weapon, Keys.WEAPON_DAMAGE_PER_ATTACK, 5); + DataTest.checkOfferData(weapon, Keys.DISABLE_SHIELD_TICKS, Ticks.of(10)); + } + + @Test + public void testShield() { + final ItemStack shield = ItemStack.of(ItemTypes.SHIELD); + DataTest.checkOfferData(shield, Keys.SHIELD_DEPLOY_TICKS, Ticks.of(15)); + DataTest.checkOfferData(shield, Keys.DISABLE_SHIELD_TICKS_SCALE, 2.5); + DataTest.checkOfferData(shield, Keys.SHIELD_DAMAGE_REDUCTIONS, List.of( + ShieldDamageReduction.of(ShieldDamageReduction.MultiplyAdd.builder() + .horizontalBlockingAngle(45) + .constantReduction(2) + .fractionalReduction(0.5) + .damageTypes(Set.of(DamageTypes.ARROW.get(), DamageTypes.PLAYER_ATTACK.get())) + .build() + ) + )); + DataTest.checkOfferData(shield, Keys.SHIELD_ITEM_DAMAGE_FUNCTION, ShieldItemDamageFunction.of( + ShieldItemDamageFunction.MultiplyAdd.builder() + .constantDamage(5) + .fractionalDamage(2) + .minAttackDamage(2.5) + .build() + ) + ); + DataTest.checkOfferData(shield, Keys.SHIELD_BLOCK_SOUND, SoundTypes.ENTITY_SHULKER_HURT.get()); + DataTest.checkOfferData(shield, Keys.SHIELD_DISABLE_SOUND, SoundTypes.ENTITY_ENDER_DRAGON_DEATH.get()); + } + + @Test + public void testEquippable() { + final ItemStack armor = ItemStack.of(ItemTypes.DIAMOND_CHESTPLATE); + DataTest.checkOfferData(armor, Keys.ALLOWED_ENTITIES, Set.of(EntityTypes.PLAYER.get(), EntityTypes.ZOMBIE.get())); + DataTest.checkOfferData(armor, Keys.CAMERA_OVERLAY, ResourceKey.minecraft("textures/misc/pumpkinblur.png")); + DataTest.checkOfferData(armor, Keys.CAN_BE_SHEARED, true); + DataTest.checkOfferData(armor, Keys.DAMAGE_ON_HURT, false); + DataTest.checkOfferData(armor, Keys.EQUIP_ON_INTERACT, true); + DataTest.checkOfferData(armor, Keys.EQUIP_SOUND, SoundTypes.ITEM_ARMOR_EQUIP_DIAMOND.get()); + DataTest.checkOfferData(armor, Keys.EQUIPMENT_TYPE, EquipmentTypes.HEAD.get()); + DataTest.checkOfferData(armor, Keys.IS_DISPENSABLE, false); + DataTest.checkOfferData(armor, Keys.IS_SWAPPABLE, false); + DataTest.checkOfferData(armor, Keys.SHEARING_SOUND, SoundTypes.ENTITY_SHEEP_SHEAR.get()); + } + @Test public void testEnchantments() { final ItemStack goldenApple = ItemStack.of(ItemTypes.ENCHANTED_GOLDEN_APPLE); @@ -74,7 +130,7 @@ public void testFood() { public void testArmor() { DataTest.checkGetData(ItemStack.of(ItemTypes.DIAMOND_LEGGINGS), Keys.ARMOR_MATERIAL, ArmorMaterials.DIAMOND.get()); DataTest.checkGetData(ItemStack.of(ItemTypes.LEATHER_BOOTS), Keys.ARMOR_MATERIAL, ArmorMaterials.LEATHER.get()); - DataTest.checkGetData(ItemStack.of(ItemTypes.TURTLE_HELMET), Keys.ARMOR_MATERIAL, ArmorMaterials.TURTLE.get()); + DataTest.checkGetData(ItemStack.of(ItemTypes.TURTLE_HELMET), Keys.ARMOR_MATERIAL, ArmorMaterials.TURTLE_SCUTE.get()); DataTest.checkGetData(ItemStack.of(ItemTypes.LEATHER_BOOTS), Keys.DAMAGE_ABSORPTION, 1.0); DataTest.checkGetData(ItemStack.of(ItemTypes.DIAMOND_CHESTPLATE), Keys.DAMAGE_ABSORPTION, 8.0); @@ -144,6 +200,7 @@ public void testWaterBucket() { DataTest.checkGetData(waterBucket, Keys.FLUID_ITEM_STACK, FluidStackSnapshot.builder().fluid(FluidTypes.WATER).build()); } + @Disabled @Test public void testDyeColor() { DataTest.checkGetData(ItemStack.of(ItemTypes.RED_BANNER), Keys.DYE_COLOR, DyeColors.RED.get()); @@ -189,6 +246,7 @@ public void testEnchantedBook() { DataTest.checkOfferData(enchantedBook, Keys.STORED_ENCHANTMENTS, List.of(Enchantment.of(EnchantmentTypes.SHARPNESS, 5), Enchantment.of(EnchantmentTypes.PROTECTION, 4))); } + @Disabled @Test public void testToolType() { DataTest.checkGetData(ItemStack.of(ItemTypes.WOODEN_AXE), Keys.TOOL_TYPE, ItemTiers.WOOD.get()); diff --git a/src/test/java/org/spongepowered/common/event/entity/DamageEventTest.java b/src/test/java/org/spongepowered/common/event/entity/DamageEventTest.java index be9a5d212bd..84b2763d56d 100644 --- a/src/test/java/org/spongepowered/common/event/entity/DamageEventTest.java +++ b/src/test/java/org/spongepowered/common/event/entity/DamageEventTest.java @@ -63,7 +63,8 @@ public void testDamageEvent() { sheep.damage(15, damageSource); - assertEquals(1, listener.events, "events count"); + // There's a Pre and Post + assertEquals(2, listener.events, "events count"); assertNotNull(listener.lastEvent, "last event"); assertEquals(15, listener.lastEvent.baseDamage(), "base damage"); assertEquals(damageSource, listener.lastEvent.cause().root(), "root cause"); diff --git a/src/test/java/org/spongepowered/common/event/tracking/context/transaction/GameTransactionIteratorTest.java b/src/test/java/org/spongepowered/common/event/tracking/context/transaction/GameTransactionIteratorTest.java index 50f31548676..c71fafba96d 100644 --- a/src/test/java/org/spongepowered/common/event/tracking/context/transaction/GameTransactionIteratorTest.java +++ b/src/test/java/org/spongepowered/common/event/tracking/context/transaction/GameTransactionIteratorTest.java @@ -50,7 +50,7 @@ public void verifyIterator() { @Test public void verifyNestedIterator() { final StubTransaction stubTransaction = new StubTransaction(); - final ResultingTransactionBySideEffect effect = new ResultingTransactionBySideEffect( + final ResultingTransactionBySideEffect effect = new ResultingTransactionBySideEffect<>( InventoryEffect.getInstance()); stubTransaction.addLast(effect); final StubTransaction child1 = new StubTransaction(); @@ -77,7 +77,7 @@ public void verifyDeeplyNestedIterator(final int effectCount, final int childCou try (final PhaseContext<@NonNull ?> phaseContext = StubPhaseState.getInstance().createPhaseContext(PhaseTracker.getInstance())) { phaseContext.buildAndSwitch(); for (int i = 0; i < effectCount; i++) { - final ResultingTransactionBySideEffect effect = new ResultingTransactionBySideEffect( + final ResultingTransactionBySideEffect effect = new ResultingTransactionBySideEffect<>( InventoryEffect.getInstance()); parent.addLast(effect); for (int j = 0; j < childCount; j++) { @@ -129,7 +129,7 @@ public void validateTransactionalIterator(final int transactionCount, final int transactions[t] = transaction; transactor.logTransaction(transaction); for (int i = 0; i < effectCount; i++) { - final ResultingTransactionBySideEffect effect = new ResultingTransactionBySideEffect( + final ResultingTransactionBySideEffect effect = new ResultingTransactionBySideEffect<>( InventoryEffect.getInstance()); try (final EffectTransactor ignored = transactor.pushEffect(effect)) { for (int j = 0; j < childCount; j++) { diff --git a/src/test/java/org/spongepowered/common/plugin/discovery/PluginDiscoveryTest.java b/src/test/java/org/spongepowered/common/plugin/discovery/PluginDiscoveryTest.java new file mode 100644 index 00000000000..b8d81311396 --- /dev/null +++ b/src/test/java/org/spongepowered/common/plugin/discovery/PluginDiscoveryTest.java @@ -0,0 +1,53 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.plugin.discovery; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.spongepowered.common.applaunch.AppLaunch; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.common.plugin.discovery.spi.DummyMetadataReader; +import org.spongepowered.plugin.PluginService; + +import java.util.List; +import java.util.Optional; + +public class PluginDiscoveryTest { + + @Test + public void verifyDummyCandidate() { + // locator + Optional candidate = AppLaunch.pluginPlatform().discovery().candidates().stream() + .filter(c -> c.resource().property("dummy_key").isPresent()).findFirst(); + assertTrue(candidate.isPresent()); + assertEquals("dummy_value", candidate.get().resource().property("dummy_key").orElse(null)); + assertIterableEquals(List.of("dummy_locator"), candidate.get().locators().stream().map(PluginService::name).toList()); + + // reader + assertTrue(candidate.get().gameResource()); + assertIterableEquals(List.of(DummyMetadataReader.DUMMY_METADATA), candidate.get().metadata()); + } +} diff --git a/src/test/java/org/spongepowered/common/plugin/discovery/spi/DummyMetadataReader.java b/src/test/java/org/spongepowered/common/plugin/discovery/spi/DummyMetadataReader.java new file mode 100644 index 00000000000..86b00487367 --- /dev/null +++ b/src/test/java/org/spongepowered/common/plugin/discovery/spi/DummyMetadataReader.java @@ -0,0 +1,68 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.plugin.discovery.spi; + +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.discovery.PluginMetadataReader; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.metadata.builtin.InheritableMetadata; +import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; +import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; + +import java.util.Collection; +import java.util.List; + +public class DummyMetadataReader implements PluginMetadataReader { + private static final VersionRange ANY_VERSION; + + static { + try { + ANY_VERSION = VersionRange.createFromVersionSpec("*"); + } catch (InvalidVersionSpecificationException e) { + throw new RuntimeException(e); + } + } + + public static final PluginMetadata DUMMY_METADATA = StandardPluginMetadata.builder().id("dummy_plugin") + .global(InheritableMetadata.builder().loader(new PluginLoaderSpecification("dummy_loader", ANY_VERSION)).version(new DefaultArtifactVersion("1.0.0")).build()).build(); + + @Override + public Collection readPluginMetadata(final Environment environment, final PluginResource resource, final List locators) { + if (resource instanceof DummyPluginResource) { + return List.of(DummyMetadataReader.DUMMY_METADATA); + } + return List.of(); + } + + @Override + public String name() { + return "dummy_reader"; + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/cooking/CookingRecipeMixin_ItemStackLike.java b/src/test/java/org/spongepowered/common/plugin/discovery/spi/DummyPluginResource.java similarity index 66% rename from src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/cooking/CookingRecipeMixin_ItemStackLike.java rename to src/test/java/org/spongepowered/common/plugin/discovery/spi/DummyPluginResource.java index fd50a0ed82d..c57ad44b255 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/recipe/cooking/CookingRecipeMixin_ItemStackLike.java +++ b/src/test/java/org/spongepowered/common/plugin/discovery/spi/DummyPluginResource.java @@ -22,23 +22,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.itemstacklike.item.recipe.cooking; +package org.spongepowered.common.plugin.discovery.spi; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.item.recipe.cooking.CookingRecipe; -import org.spongepowered.api.item.recipe.cooking.CookingResult; -import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.plugin.discovery.PluginResource; +import java.net.URI; +import java.nio.file.Path; +import java.util.List; import java.util.Optional; -@Mixin(value = CookingRecipe.class, remap = false) -public interface CookingRecipeMixin_ItemStackLike { +/** + * WARNING: This class may exist in multiple classloaders. + */ +public class DummyPluginResource implements PluginResource { + + @Override + public List paths() { + return List.of(); + } - default boolean isValid(ItemStackSnapshot ingredient) { - return ((CookingRecipe) this).isValid(ingredient); + @Override + public Optional property(final String key) { + return "dummy_key".equals(key) ? Optional.of("dummy_value") : Optional.empty(); } - default Optional result(ItemStackSnapshot ingredient) { - return ((CookingRecipe) this).result(ingredient); + @Override + public Optional locate(final String path) { + return Optional.empty(); } } diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/reader/PluginFileReader.java b/src/test/java/org/spongepowered/common/plugin/discovery/spi/DummyResourceLocator.java similarity index 65% rename from neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/reader/PluginFileReader.java rename to src/test/java/org/spongepowered/common/plugin/discovery/spi/DummyResourceLocator.java index 899952076da..8f7c05a1790 100644 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/loading/moddiscovery/reader/PluginFileReader.java +++ b/src/test/java/org/spongepowered/common/plugin/discovery/spi/DummyResourceLocator.java @@ -22,22 +22,27 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge.applaunch.loading.moddiscovery.reader; +package org.spongepowered.common.plugin.discovery.spi; -import cpw.mods.jarhandling.JarContents; -import net.neoforged.neoforgespi.locating.IModFile; -import net.neoforged.neoforgespi.locating.IModFileReader; -import net.neoforged.neoforgespi.locating.ModFileDiscoveryAttributes; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.discovery.UnknownResourceStrategy; + +import java.util.Collection; +import java.util.List; + +/** + * WARNING: This class may exist in multiple classloaders. + */ +public class DummyResourceLocator implements PluginResourceLocator { -public class PluginFileReader implements IModFileReader { @Override - public @Nullable IModFile read(final JarContents jar, final ModFileDiscoveryAttributes attributes) { - return PluginFileParser.newPluginInstance(jar, attributes.withReader(this)); + public Collection locatePluginResources(final Environment environment) { + return List.of(new Result(new DummyPluginResource(), UnknownResourceStrategy.IGNORE)); } @Override - public String toString() { - return "sponge manifest"; + public String name() { + return "dummy_locator"; } } diff --git a/src/test/java/org/spongepowered/common/plugin/discovery/spi/package-info.java b/src/test/java/org/spongepowered/common/plugin/discovery/spi/package-info.java new file mode 100644 index 00000000000..ebbacf576e3 --- /dev/null +++ b/src/test/java/org/spongepowered/common/plugin/discovery/spi/package-info.java @@ -0,0 +1,6 @@ +/** + * WARNING: + * Classes in this package exist in two classloaders: a discovery batch classloader and the game classloader. + * Don't attempt to cast instances across different classloaders. + */ +package org.spongepowered.common.plugin.discovery.spi; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/slot/SidedSlotMixin_ItemStackLike.java b/src/test/java/org/spongepowered/common/plugin/entrypoint/EntrypointPlugin_Main.java similarity index 61% rename from src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/slot/SidedSlotMixin_ItemStackLike.java rename to src/test/java/org/spongepowered/common/plugin/entrypoint/EntrypointPlugin_Main.java index 4a411b0d7f8..04d8c52112a 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/slot/SidedSlotMixin_ItemStackLike.java +++ b/src/test/java/org/spongepowered/common/plugin/entrypoint/EntrypointPlugin_Main.java @@ -22,25 +22,30 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory.slot; +package org.spongepowered.common.plugin.entrypoint; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.slot.SidedSlot; -import org.spongepowered.api.util.Direction; -import org.spongepowered.asm.mixin.Mixin; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Mixin(value = SidedSlot.class, remap = false) -public interface SidedSlotMixin_ItemStackLike { +import com.google.inject.Inject; +import org.spongepowered.api.Game; +import org.spongepowered.api.Sponge; - default boolean canAccept(ItemStack stack, Direction from) { - return ((SidedSlot) this).canAccept(stack, from); - } +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +public class EntrypointPlugin_Main { + public static final AtomicInteger INSTANCES_COUNT = new AtomicInteger(); + public static final AtomicReference INSTANCE = new AtomicReference<>(); + + @Inject + private Game game; - default boolean offer(ItemStack stack, Direction from) { - return ((SidedSlot) this).offer(stack, from); + public EntrypointPlugin_Main() { + EntrypointPlugin_Main.INSTANCES_COUNT.incrementAndGet(); + EntrypointPlugin_Main.INSTANCE.set(this); } - default boolean canGet(ItemStack stack, Direction from) { - return ((SidedSlot) this).canGet(stack, from); + public void test() { + assertEquals(Sponge.game(), this.game); } } diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/VanillaJavaPluginContainer.java b/src/test/java/org/spongepowered/common/plugin/entrypoint/EntrypointPlugin_Server.java similarity index 58% rename from vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/VanillaJavaPluginContainer.java rename to src/test/java/org/spongepowered/common/plugin/entrypoint/EntrypointPlugin_Server.java index 696596282af..7856bf50da5 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/VanillaJavaPluginContainer.java +++ b/src/test/java/org/spongepowered/common/plugin/entrypoint/EntrypointPlugin_Server.java @@ -22,36 +22,35 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.launch.plugin; +package org.spongepowered.common.plugin.entrypoint; -import com.google.inject.Injector; -import org.spongepowered.api.Sponge; -import org.spongepowered.common.launch.plugin.SpongePluginContainer; -import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.builtin.StandardPluginContainer; +import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.Optional; +import com.google.inject.Inject; +import org.spongepowered.api.Game; +import org.spongepowered.api.Server; +import org.spongepowered.api.Sponge; -public final class VanillaJavaPluginContainer extends StandardPluginContainer implements SpongePluginContainer { +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; - private Injector injector; +public class EntrypointPlugin_Server { + public static final AtomicInteger INSTANCES_COUNT = new AtomicInteger(); + public static final AtomicReference INSTANCE = new AtomicReference<>(); - public VanillaJavaPluginContainer(final PluginCandidate candidate) { - super(candidate); - } + @Inject + private Game game; - @Override - protected void initializeInstance(final Object instance) { - super.initializeInstance(instance); - Sponge.eventManager().registerListeners(this, instance); - } + @Inject + private Server server; - @Override - public Optional injector() { - return Optional.ofNullable(this.injector); + public EntrypointPlugin_Server() { + EntrypointPlugin_Server.INSTANCES_COUNT.incrementAndGet(); + EntrypointPlugin_Server.INSTANCE.set(this); } - public void setInjector(final Injector injector) { - this.injector = injector; + public void test() { + assertEquals(Sponge.game(), this.game); + assertEquals(Sponge.server(), this.server); } } diff --git a/src/accessors/java/org/spongepowered/common/accessor/server/level/TicketTypeAccessor.java b/src/test/java/org/spongepowered/common/plugin/entrypoint/PluginEntrypointTest.java similarity index 69% rename from src/accessors/java/org/spongepowered/common/accessor/server/level/TicketTypeAccessor.java rename to src/test/java/org/spongepowered/common/plugin/entrypoint/PluginEntrypointTest.java index bcbe0aa9f11..9cf8e696d5f 100644 --- a/src/accessors/java/org/spongepowered/common/accessor/server/level/TicketTypeAccessor.java +++ b/src/test/java/org/spongepowered/common/plugin/entrypoint/PluginEntrypointTest.java @@ -22,20 +22,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.accessor.server.level; +package org.spongepowered.common.plugin.entrypoint; -import net.minecraft.server.level.TicketType; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; -import org.spongepowered.common.UntransformedInvokerError; +import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.Comparator; +import org.junit.jupiter.api.Test; -@Mixin(TicketType.class) -public interface TicketTypeAccessor { +public class PluginEntrypointTest { - @Invoker("") - static TicketType accessor$createInstance(final String name, final Comparator comparator, final long lifetime) { - throw new UntransformedInvokerError(); + @Test + public void verifyMainEntrypoint() { + assertEquals(1, EntrypointPlugin_Main.INSTANCES_COUNT.get()); + EntrypointPlugin_Main.INSTANCE.get().test(); + } + + @Test + public void verifyServerEntrypoint() { + assertEquals(1, EntrypointPlugin_Server.INSTANCES_COUNT.get()); + EntrypointPlugin_Server.INSTANCE.get().test(); } } diff --git a/src/test/java/org/spongepowered/common/plugin/loader/PluginLoaderTest.java b/src/test/java/org/spongepowered/common/plugin/loader/PluginLoaderTest.java new file mode 100644 index 00000000000..3fa33cdfdbd --- /dev/null +++ b/src/test/java/org/spongepowered/common/plugin/loader/PluginLoaderTest.java @@ -0,0 +1,46 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.plugin.loader; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.spongepowered.api.Sponge; +import org.spongepowered.common.plugin.discovery.spi.DummyMetadataReader; +import org.spongepowered.common.plugin.loader.spi.DummyPluginContainer; +import org.spongepowered.plugin.PluginContainer; + +import java.util.Optional; + +public class PluginLoaderTest { + + @Test + public void verifyDummyContainer() { + final Optional container = Sponge.pluginManager().plugin("dummy_plugin"); + assertTrue(container.isPresent()); + assertInstanceOf(DummyPluginContainer.class, container.get()); + assertEquals(DummyMetadataReader.DUMMY_METADATA, container.get().metadata()); + } +} diff --git a/src/main/java/org/spongepowered/common/tag/SpongeTagTemplateFactory.java b/src/test/java/org/spongepowered/common/plugin/loader/spi/DummyPluginContainer.java similarity index 71% rename from src/main/java/org/spongepowered/common/tag/SpongeTagTemplateFactory.java rename to src/test/java/org/spongepowered/common/plugin/loader/spi/DummyPluginContainer.java index a50a9f943c0..049ca2cbfe6 100644 --- a/src/main/java/org/spongepowered/common/tag/SpongeTagTemplateFactory.java +++ b/src/test/java/org/spongepowered/common/plugin/loader/spi/DummyPluginContainer.java @@ -22,19 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.tag; +package org.spongepowered.common.plugin.loader.spi; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.tag.TagTemplate; -import org.spongepowered.api.tag.Taggable; +import org.spongepowered.plugin.builtin.StandardPluginContainer; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; -import java.util.Objects; - -public final class SpongeTagTemplateFactory implements TagTemplate.Factory { - - @Override - public > TagTemplate.Builder builder(final DataPack> pack) { - return new SpongeTagTemplateBuilder<>(Objects.requireNonNull(pack, "tagType")); +/** + * Technically not needed to load a plugin, but exist for the tests. + */ +public class DummyPluginContainer extends StandardPluginContainer { + public DummyPluginContainer(final PluginResource resource, final PluginMetadata metadata) { + super(resource, metadata); } - } diff --git a/src/test/java/org/spongepowered/common/plugin/loader/spi/DummyPluginLoader.java b/src/test/java/org/spongepowered/common/plugin/loader/spi/DummyPluginLoader.java new file mode 100644 index 00000000000..4b277d4cfb0 --- /dev/null +++ b/src/test/java/org/spongepowered/common/plugin/loader/spi/DummyPluginLoader.java @@ -0,0 +1,50 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.plugin.loader.spi; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.PluginLoader; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; + +public class DummyPluginLoader implements PluginLoader { + @Override + public ArtifactVersion version() { + return new DefaultArtifactVersion("1.0.0"); + } + + @Override + public PluginContainer loadPlugin(final Environment environment, final PluginResource resource, final PluginMetadata metadata) { + return new DummyPluginContainer(resource, metadata); + } + + @Override + public String name() { + return "dummy_loader"; + } +} diff --git a/src/test/java/org/spongepowered/common/registry/RegistryTest.java b/src/test/java/org/spongepowered/common/registry/RegistryTest.java index 7caae8a0c58..552cb05df9d 100644 --- a/src/test/java/org/spongepowered/common/registry/RegistryTest.java +++ b/src/test/java/org/spongepowered/common/registry/RegistryTest.java @@ -29,14 +29,21 @@ import com.google.common.reflect.TypeToken; import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; -import org.spongepowered.api.command.registrar.tree.CommandCompletionProviders; -import org.spongepowered.api.data.type.BoatTypes; -import org.spongepowered.api.registry.*; +import org.spongepowered.api.data.type.CopperGolemPoses; +import org.spongepowered.api.data.type.CopperOxidations; +import org.spongepowered.api.data.type.SideChains; +import org.spongepowered.api.data.type.WolfSoundVariants; +import org.spongepowered.api.registry.DefaultedRegistryReference; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryEntry; +import org.spongepowered.api.registry.RegistryRoots; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.api.scoreboard.CollisionRules; +import org.spongepowered.api.scoreboard.Visibilities; import org.spongepowered.api.util.annotation.CatalogedBy; import java.lang.reflect.Field; @@ -46,6 +53,7 @@ import java.util.Collection; import java.util.LinkedHashSet; import java.util.Optional; +import java.util.Set; import java.util.stream.Stream; public class RegistryTest { @@ -93,10 +101,13 @@ private static Stream streamDefaultedReferenceFields() { @TestFactory public Stream generateDefaultedReferenceTests() { + final Set> ignored = Set.of( + CollisionRules.class, CopperGolemPoses.class, CopperOxidations.class, + SideChains.class, Visibilities.class, WolfSoundVariants.class); // TODO fix return RegistryTest.streamDefaultedReferenceFields().map(field -> { - Assumptions.assumeFalse(field.getDeclaringClass() == BoatTypes.class || field.getDeclaringClass() == CommandCompletionProviders.class); final String name = "Field " + field.getDeclaringClass().getSimpleName() + "#" + field.getName(); return dynamicTest(name, () -> { + Assumptions.assumeFalse(ignored.contains(field.getDeclaringClass())); final DefaultedRegistryReference ref = (DefaultedRegistryReference) field.get(null); assertNotNull(ref.get()); }); @@ -114,7 +125,6 @@ private static Stream> streamApiInterfaces(final Object instance) { .filter(clazz -> clazz.getName().startsWith("org.spongepowered.api.")); } - @Disabled @TestFactory public Stream generateMethodTests() { return RegistryTest.streamRegistries().flatMap(Registry::streamEntries) diff --git a/src/test/java/org/spongepowered/common/scheduler/SchedulerTest.java b/src/test/java/org/spongepowered/common/scheduler/SchedulerTest.java index 1ac7b72f764..f00e8a14038 100644 --- a/src/test/java/org/spongepowered/common/scheduler/SchedulerTest.java +++ b/src/test/java/org/spongepowered/common/scheduler/SchedulerTest.java @@ -29,11 +29,7 @@ import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; import org.spongepowered.api.Server; import org.spongepowered.api.Sponge; import org.spongepowered.api.scheduler.Task; @@ -44,8 +40,6 @@ import java.util.concurrent.atomic.AtomicBoolean; -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) public class SchedulerTest { @Test diff --git a/src/test/java/org/spongepowered/common/test/TestInvocationInterceptor.java b/src/test/java/org/spongepowered/common/test/TestInvocationInterceptor.java index cd848e568d7..2a1b9fdd69a 100644 --- a/src/test/java/org/spongepowered/common/test/TestInvocationInterceptor.java +++ b/src/test/java/org/spongepowered/common/test/TestInvocationInterceptor.java @@ -88,13 +88,13 @@ public void interceptAfterEachMethod(final Invocation invocation, final Re private T intercept(final Invocation invocation) throws Throwable { try ( final PhaseContext<@NonNull ?> context = TickPhase.Tick.SERVER_TICK - .createPhaseContext(PhaseTracker.SERVER) + .createPhaseContext(PhaseTracker.getServerInstanceExplicitly()) .server((MinecraftServer) Sponge.server()) ) { context.buildAndSwitch(); return invocation.proceed(); } finally { - PhaseTracker.SERVER.ensureEmpty(); + PhaseTracker.getServerInstanceExplicitly().ensureEmpty(); } } } diff --git a/src/test/java/org/spongepowered/common/util/ExperienceHolderUtilTest.java b/src/test/java/org/spongepowered/common/util/ExperienceHolderUtilTest.java index e0e20b4ed5c..2832b5c67aa 100644 --- a/src/test/java/org/spongepowered/common/util/ExperienceHolderUtilTest.java +++ b/src/test/java/org/spongepowered/common/util/ExperienceHolderUtilTest.java @@ -25,17 +25,17 @@ package org.spongepowered.common.util; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import com.mojang.authlib.GameProfile; -import net.minecraft.core.BlockPos; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; -import net.minecraft.world.level.storage.PrimaryLevelData; +import org.jetbrains.annotations.Nullable; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.world.DefaultWorldKeys; import java.util.ArrayList; import java.util.List; @@ -44,10 +44,14 @@ public final class ExperienceHolderUtilTest { public static List testExp() { - final Level world = mock(Level.class); - when(world.getLevelData()).thenReturn(mock(PrimaryLevelData.class)); + final Level world = (Level) Sponge.server().worldManager().world(DefaultWorldKeys.DEFAULT).get(); + + final Player player = new Player(world, new GameProfile(UUID.randomUUID(), "Player")) { + @Override + public @Nullable GameType gameMode() { + return null; + } - final Player player = new Player(world, BlockPos.ZERO, 0, new GameProfile(UUID.randomUUID(), "Player")) { @Override public boolean isSpectator() { return false; diff --git a/src/test/java/org/spongepowered/common/util/transformation/VolumeTransformationTest.java b/src/test/java/org/spongepowered/common/util/transformation/VolumeTransformationTest.java index 759ba182b96..d5250df6641 100644 --- a/src/test/java/org/spongepowered/common/util/transformation/VolumeTransformationTest.java +++ b/src/test/java/org/spongepowered/common/util/transformation/VolumeTransformationTest.java @@ -25,13 +25,9 @@ package org.spongepowered.common.util.transformation; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; import org.spongepowered.api.Sponge; import org.spongepowered.api.block.BlockTypes; import org.spongepowered.api.block.entity.BlockEntityArchetype; @@ -54,8 +50,6 @@ import java.util.Optional; import java.util.stream.Stream; -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) public final class VolumeTransformationTest { private static final Vector3i INVALID_STUB_POSITION = Vector3i.from( diff --git a/src/test/java/org/spongepowered/common/world/border/WorldBorderTest.java b/src/test/java/org/spongepowered/common/world/border/WorldBorderTest.java new file mode 100644 index 00000000000..2cc37d67283 --- /dev/null +++ b/src/test/java/org/spongepowered/common/world/border/WorldBorderTest.java @@ -0,0 +1,74 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.border; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.world.DefaultWorldKeys; +import org.spongepowered.api.world.border.WorldBorder; +import org.spongepowered.api.world.server.ServerWorld; + +public class WorldBorderTest { + + @Test + public void testBorderIsApplied() { + final ServerWorld world = Sponge.server().worldManager().world(DefaultWorldKeys.DEFAULT).get(); + final WorldBorder worldBorder = world.border(); + final WorldBorder border = WorldBorder.builder().center(1, 1).initialDiameter(1).build(); + + try { + world.setBorder(border); + + Assertions.assertEquals(border, world.border()); + } finally { + world.setBorder(worldBorder); + } + } + + @Test + public void testBorderIsWorldSpecific() { + final ServerWorld world = Sponge.server().worldManager().world(DefaultWorldKeys.DEFAULT).get(); + final ServerWorld nether = Sponge.server().worldManager().world(DefaultWorldKeys.THE_NETHER).get(); + + final WorldBorder worldBorder = world.border(); + final WorldBorder worldBorderNether = nether.border(); + + final WorldBorder border = WorldBorder.builder().center(1, 1).initialDiameter(1).build(); + final WorldBorder netherBorder = WorldBorder.builder().center(2, 2).initialDiameter(2).build(); + + try { + world.setBorder(border); + nether.setBorder(netherBorder); + + Assertions.assertEquals(border, world.border()); + Assertions.assertEquals(netherBorder, nether.border()); + } finally { + world.setBorder(worldBorder); + nether.setBorder(worldBorderNether); + } + } + +} diff --git a/src/test/resource-templates/META-INF/sponge_test_plugins.json b/src/test/resource-templates/META-INF/sponge_test_plugins.json new file mode 100644 index 00000000000..c426e175cb4 --- /dev/null +++ b/src/test/resource-templates/META-INF/sponge_test_plugins.json @@ -0,0 +1,35 @@ +{ + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "MIT", + "global": { + "version": "{{ apiVersion }}", + "links": { + "homepage": "https://www.spongepowered.org", + "source": "https://www.spongepowered.org/source", + "issues": "https://www.spongepowered.org/issues" + }, + "contributors": [ + { + "name": "SpongePowered", + "description": "Lead Developer" + } + ] + }, + "plugins": [ + { + "id": "entrypoint", + "description": "Verifies entrypoints are properly called.", + "entrypoints": { + "main": [ + "org.spongepowered.common.plugin.entrypoint.EntrypointPlugin_Main" + ], + "server": [ + "org.spongepowered.common.plugin.entrypoint.EntrypointPlugin_Server" + ] + } + } + ] +} diff --git a/src/test/resources/META-INF/services/org.spongepowered.plugin.PluginLoader b/src/test/resources/META-INF/services/org.spongepowered.plugin.PluginLoader new file mode 100644 index 00000000000..8f4bfeae9dc --- /dev/null +++ b/src/test/resources/META-INF/services/org.spongepowered.plugin.PluginLoader @@ -0,0 +1,2 @@ +org.spongepowered.common.launch.plugin.loader.JavaPluginLoader +org.spongepowered.common.plugin.loader.spi.DummyPluginLoader diff --git a/src/test/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginMetadataReader b/src/test/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginMetadataReader new file mode 100644 index 00000000000..216d458b97d --- /dev/null +++ b/src/test/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginMetadataReader @@ -0,0 +1 @@ +org.spongepowered.common.plugin.discovery.spi.DummyMetadataReader diff --git a/src/test/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator b/src/test/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator new file mode 100644 index 00000000000..9686213daee --- /dev/null +++ b/src/test/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator @@ -0,0 +1 @@ +org.spongepowered.common.plugin.discovery.spi.DummyResourceLocator diff --git a/testplugins/src/main/java/org/spongepowered/test/advancement/AdvancementTest.java b/testplugins/src/main/java/org/spongepowered/test/advancement/AdvancementTest.java index 78e61eb768f..34e9cbeeef4 100644 --- a/testplugins/src/main/java/org/spongepowered/test/advancement/AdvancementTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/advancement/AdvancementTest.java @@ -29,8 +29,8 @@ import org.apache.logging.log4j.Logger; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; +import org.spongepowered.api.advancement.Advancement; import org.spongepowered.api.advancement.AdvancementProgress; -import org.spongepowered.api.advancement.AdvancementTemplate; import org.spongepowered.api.advancement.AdvancementTree; import org.spongepowered.api.advancement.AdvancementTypes; import org.spongepowered.api.advancement.DisplayInfo; @@ -43,7 +43,6 @@ import org.spongepowered.api.advancement.criteria.trigger.FilteredTriggerConfiguration; import org.spongepowered.api.advancement.criteria.trigger.Trigger; import org.spongepowered.api.block.BlockTypes; -import org.spongepowered.api.command.exception.CommandException; import org.spongepowered.api.command.parameter.CommandContext; import org.spongepowered.api.data.persistence.AbstractDataBuilder; import org.spongepowered.api.data.persistence.DataContainer; @@ -58,13 +57,15 @@ import org.spongepowered.api.event.filter.cause.First; import org.spongepowered.api.event.item.inventory.ChangeInventoryEvent; import org.spongepowered.api.event.item.inventory.container.InteractContainerEvent; -import org.spongepowered.api.event.lifecycle.RegisterDataPackValueEvent; +import org.spongepowered.api.event.lifecycle.RegisterDataEvent; import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; import org.spongepowered.api.item.ItemTypes; import org.spongepowered.api.item.inventory.BlockCarrier; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.item.inventory.query.QueryTypes; import org.spongepowered.api.item.inventory.type.CarriedInventory; +import org.spongepowered.api.registry.DefaultedRegistryReference; +import org.spongepowered.api.registry.RegistryRegistrationSet; import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.builtin.jvm.Plugin; @@ -75,50 +76,194 @@ @Plugin("advancementtest") public final class AdvancementTest implements LoadableModule { + public static final String NAMESPACE = "advancementtest"; + + public static final class Triggers { + + public static final class Holder { + + private static final RegistryRegistrationSet> TRIGGERS = Triggers.TRIGGER_BUILDER.build(); + } + + private static final RegistryRegistrationSet.Builder> TRIGGER_BUILDER = RegistryRegistrationSet.builder(RegistryTypes.TRIGGER, Sponge::game); + + public static final DefaultedRegistryReference> INVENTORY_CHANGE_TRIGGER = Triggers.TRIGGER_BUILDER.register( + ResourceKey.of(AdvancementTest.NAMESPACE, "my_inventory_trigger"), + () -> Trigger.builder() + .dataSerializableConfig(InventoryChangeTriggerConfig.class) + .listener(triggerEvent -> { + final ItemStack stack = triggerEvent.trigger().configuration().stack; + final int found = triggerEvent.player().inventory().query(QueryTypes.ITEM_STACK_IGNORE_QUANTITY, stack).totalQuantity(); + triggerEvent.setResult(stack.quantity() <= found); + }) + .name("my_inventory_trigger") + .build() + ); + } + + public static final class AdvancementCriteria { + + public static final AdvancementCriterion SOME_DIRT = AdvancementCriterion.builder().trigger( + Triggers.INVENTORY_CHANGE_TRIGGER.get(), + FilteredTrigger.builder() + .config(new InventoryChangeTriggerConfig(ItemStack.of(ItemTypes.DIRT))) + .build()).name("some_dirt").build(); + + public static final AdvancementCriterion LOTS_OF_DIRT = AdvancementCriterion.builder().trigger( + Triggers.INVENTORY_CHANGE_TRIGGER.get(), + FilteredTrigger.builder() + .config(new InventoryChangeTriggerConfig(ItemStack.of(ItemTypes.DIRT, 64))) + .build()).name("lots_of_dirt").build(); + + public static final AdvancementCriterion TONS_OF_DIRT = AdvancementCriterion.builder().trigger( + Triggers.INVENTORY_CHANGE_TRIGGER.get(), + FilteredTrigger.builder() + .config(new InventoryChangeTriggerConfig(ItemStack.of(ItemTypes.DIRT, 64*9))) + .build()).name("tons_of_dirt").build(); + + public static final ScoreAdvancementCriterion COUNTER_1 = ScoreAdvancementCriterion.builder().goal(10).name("counter").build(); + public static final AdvancementCriterion COUNTER_1_BYPASS = AdvancementCriterion.dummy(); + + public static final ScoreAdvancementCriterion COUNTER_2 = ScoreAdvancementCriterion.builder().goal(20).name("counter").build(); + + public static final AdvancementCriterion A = AdvancementCriterion.builder().name("A").build(); + public static final AdvancementCriterion B = AdvancementCriterion.builder().name("B").build(); + public static final AdvancementCriterion C = AdvancementCriterion.builder().name("C").build(); + public static final AdvancementCriterion D = AdvancementCriterion.builder().name("D").build(); + public static final AdvancementCriterion E = AdvancementCriterion.builder().name("E").build(); + public static final AdvancementCriterion F = AdvancementCriterion.builder().name("F").build(); + + public static final AdvancementCriterion COMBINATION = OrCriterion.of( + AdvancementCriteria.A, + AndCriterion.of(AdvancementCriteria.B, OrCriterion.of(AdvancementCriteria.C, AdvancementCriteria.D)), + AndCriterion.of(AdvancementCriteria.E, AdvancementCriteria.F)); + } + + public static final class Advancements { + + public static final class Holder { + + private static final RegistryRegistrationSet ADVANCEMENTS = Advancements.ADVANCEMENT_BUILDER.build(); + } + + private static final RegistryRegistrationSet.Builder ADVANCEMENT_BUILDER = RegistryRegistrationSet.builder(RegistryTypes.ADVANCEMENT, Sponge::server); + + public static final DefaultedRegistryReference ROOT_ADVANCEMENT = Advancements.ADVANCEMENT_BUILDER.register( + ResourceKey.of(AdvancementTest.NAMESPACE, "root"), + () -> Advancement.builder() + .criterion(AdvancementCriterion.dummy()) + .displayInfo(DisplayInfo.builder() + .icon(ItemTypes.COMMAND_BLOCK) + .title(Component.text("Advancement Tests")) + .description(Component.text("Dummy trigger. Granted manually after testplugin is enabled")) + .build()) + .root().background(ResourceKey.minecraft("textures/gui/advancements/backgrounds/stone.png")) + .build()); + + public static final DefaultedRegistryReference SOME_DIRT = Advancements.ADVANCEMENT_BUILDER.register( + ResourceKey.of(AdvancementTest.NAMESPACE, "some_dirt"), + () -> Advancement.builder() + .criterion(AdvancementCriteria.SOME_DIRT) + .displayInfo(DisplayInfo.builder() + .icon(ItemTypes.DIRT) + .title(Component.text("Got dirt!")) + .type(AdvancementTypes.TASK) + .build()) + .parent(Advancements.ROOT_ADVANCEMENT.location()) + .build()); + + public static final DefaultedRegistryReference LOTS_OF_DIRT = Advancements.ADVANCEMENT_BUILDER.register( + ResourceKey.of(AdvancementTest.NAMESPACE, "lots_of_dirt"), + () -> Advancement.builder() + .criterion(AdvancementCriteria.LOTS_OF_DIRT) + .displayInfo(DisplayInfo.builder() + .icon(ItemTypes.DIRT) + .title(Component.text("Got more dirt!")) + .type(AdvancementTypes.GOAL) + .build()) + .parent(Advancements.SOME_DIRT.location()) + .build()); + + public static final DefaultedRegistryReference TONS_OF_DIRT = Advancements.ADVANCEMENT_BUILDER.register( + ResourceKey.of(AdvancementTest.NAMESPACE, "tons_of_dirt"), + () -> Advancement.builder() + .criterion(AdvancementCriteria.TONS_OF_DIRT) + .displayInfo(DisplayInfo.builder() + .icon(ItemTypes.DIRT) + .title(Component.text("Got tons of dirt!")) + .type(AdvancementTypes.CHALLENGE) + .hidden(true) + .build()) + .parent(Advancements.LOTS_OF_DIRT.location()) + .build()); + + public static final DefaultedRegistryReference COUNTER_1 = Advancements.ADVANCEMENT_BUILDER.register( + ResourceKey.of(AdvancementTest.NAMESPACE, "counting"), + () -> Advancement.builder() + .criterion(OrCriterion.of(AdvancementCriteria.COUNTER_1, AdvancementCriteria.COUNTER_1_BYPASS)) + .displayInfo(DisplayInfo.builder() + .icon(ItemTypes.CHEST) + .title(Component.text("Open some chests.")) + .type(AdvancementTypes.GOAL) + .build()) + .parent(Advancements.ROOT_ADVANCEMENT.location()) + .build()); + + public static final DefaultedRegistryReference COUNTER_2 = Advancements.ADVANCEMENT_BUILDER.register( + ResourceKey.of(AdvancementTest.NAMESPACE, "counting_more"), + () -> Advancement.builder() + .criterion(AdvancementCriteria.COUNTER_2) + .displayInfo(DisplayInfo.builder() + .icon(ItemTypes.CHEST) + .title(Component.text("Open more chests")) + .type(AdvancementTypes.CHALLENGE) + .build()) + .parent(Advancements.COUNTER_1.location()) + .build()); + + public static final DefaultedRegistryReference COMBINATION = Advancements.ADVANCEMENT_BUILDER.register( + ResourceKey.of(AdvancementTest.NAMESPACE, "combination"), + () -> Advancement.builder() + .criterion(AdvancementCriteria.COMBINATION) + .displayInfo(DisplayInfo.builder() + .icon(ItemTypes.CHEST) + .title(Component.text("A || (B & (C || D)) || (E & F)")) + .description(Component.text("ABE ABF ACDE ACDF")) + .type(AdvancementTypes.CHALLENGE) + .build()) + .parent(Advancements.COUNTER_1.location()) + .build()); + } + @Inject private PluginContainer plugin; @Inject private Logger logger; private boolean enabled = false; - private AdvancementTemplate rootAdvancement; - private Trigger inventoryChangeTrigger; private TriggerListeners listeners = new TriggerListeners(); - private ScoreAdvancementCriterion counter1; - private AdvancementCriterion counter1Bypass; - private ScoreAdvancementCriterion counter2; - private AdvancementTemplate counterAdvancement1; - private AdvancementTemplate counterAdvancement2; @Override public void enable(final CommandContext ctx) { this.enabled = true; Sponge.eventManager().registerListeners(this.plugin, this.listeners); - try { - Sponge.server().commandManager().process("reload"); - } catch (final CommandException e) { - e.printStackTrace(); - } - ctx.cause().first(ServerPlayer.class).map(player -> player.progress(this.rootAdvancement).grant()); + Sponge.server().dataPackManager().reload().join(); + ctx.cause().first(ServerPlayer.class).map(player -> player.progress(Advancements.ROOT_ADVANCEMENT.get()).grant()); } @Override public void disable(final CommandContext ctx) { this.enabled = false; Sponge.eventManager().unregisterListeners(this.listeners); - try { - Sponge.server().commandManager().process("reload"); - } catch (final CommandException e) { - e.printStackTrace(); - } + Sponge.server().dataPackManager().reload(); } @Listener private void onTreeAdjust(final AdvancementTreeEvent.GenerateLayout event) { final AdvancementTree tree = event.tree(); - if (this.rootAdvancement != null && tree.key().equals(this.rootAdvancement.key())) { - final TreeLayoutElement layoutElement1 = tree.layoutElement(this.counterAdvancement1).get(); - final TreeLayoutElement layoutElement2 = tree.layoutElement(this.counterAdvancement2).get(); + Advancements.ROOT_ADVANCEMENT.find(event.registryHolder()).filter(tree.rootAdvancement()::equals).ifPresent(advancement -> { + final TreeLayoutElement layoutElement1 = tree.layoutElement(Advancements.COUNTER_1.get(event.registryHolder())).get(); + final TreeLayoutElement layoutElement2 = tree.layoutElement(Advancements.COUNTER_2.get(event.registryHolder())).get(); layoutElement1.setPosition(layoutElement2.position()); layoutElement2.setPosition(layoutElement2.position().add(-1,2)); - } + }); } @Listener @@ -137,170 +282,43 @@ private void onTrigger(final CriterionEvent.Trigger event) { } @Listener - private void onTriggerRegistry(final RegisterRegistryValueEvent.BuiltIn> event) { + private void onTriggerRegistry(final RegisterDataEvent event) { Sponge.dataManager().registerBuilder(InventoryChangeTriggerConfig.class, new InventoryChangeTriggerConfig.Builder()); - this.inventoryChangeTrigger = Trigger.builder() - .dataSerializableConfig(InventoryChangeTriggerConfig.class) - .listener(triggerEvent -> { - final ItemStack stack = triggerEvent.trigger().configuration().stack; - final int found = triggerEvent.player().inventory().query(QueryTypes.ITEM_STACK_IGNORE_QUANTITY, stack).totalQuantity(); - triggerEvent.setResult(stack.quantity() <= found); - }) - .name("my_inventory_trigger") - .build(); - event.registry(RegistryTypes.TRIGGER).register(ResourceKey.of(this.plugin, "my_inventory_trigger"), this.inventoryChangeTrigger); } @Listener - private void onAdvancementRegistry(final RegisterDataPackValueEvent event) { + private void onRegisterRegistryValueEvent(final RegisterRegistryValueEvent event) { if (!this.enabled) { return; } - AdvancementTemplate rootAdvancement = AdvancementTemplate.builder() - .criterion(AdvancementCriterion.dummy()) - .displayInfo(DisplayInfo.builder() - .icon(ItemTypes.COMMAND_BLOCK) - .title(Component.text("Advancement Tests")) - .description(Component.text("Dummy trigger. Granted manually after testplugin is enabled")) - .build()) - .root().background(ResourceKey.minecraft("textures/gui/advancements/backgrounds/stone.png")) - .key(ResourceKey.of(this.plugin, "root")) - .build(); - event.register(rootAdvancement); - this.rootAdvancement = rootAdvancement; - - final AdvancementCriterion someDirtCriterion = AdvancementCriterion.builder().trigger( - this.inventoryChangeTrigger, - FilteredTrigger.builder() - .config(new InventoryChangeTriggerConfig(ItemStack.of(ItemTypes.DIRT))) - .build() - ).name("some_dirt").build(); - - final AdvancementTemplate someDirt = AdvancementTemplate.builder() - .criterion(someDirtCriterion) - .displayInfo(DisplayInfo.builder() - .icon(ItemTypes.DIRT) - .title(Component.text("Got dirt!")) - .type(AdvancementTypes.TASK) - .build()) - .parent(this.rootAdvancement) - .key(ResourceKey.of(this.plugin, "some_dirt")) - .build(); - event.register(someDirt); - - final AdvancementCriterion lotsOfDirtCriterion = AdvancementCriterion.builder().trigger( - this.inventoryChangeTrigger, - FilteredTrigger.builder() - .config(new InventoryChangeTriggerConfig(ItemStack.of(ItemTypes.DIRT, 64))) - .build() - ).name("lots_of_dirt").build(); - - final AdvancementTemplate lotsOfDirt = AdvancementTemplate.builder() - .criterion(lotsOfDirtCriterion) - .displayInfo(DisplayInfo.builder() - .icon(ItemTypes.DIRT) - .title(Component.text("Got more dirt!")) - .type(AdvancementTypes.GOAL) - .build()) - .parent(someDirt) - .key(ResourceKey.of(this.plugin, "lots_of_dirt")) - .build(); - event.register(lotsOfDirt); - - final AdvancementCriterion tonsOfDirtCriterion = AdvancementCriterion.builder().trigger( - this.inventoryChangeTrigger, - FilteredTrigger.builder() - .config(new InventoryChangeTriggerConfig(ItemStack.of(ItemTypes.DIRT, 64*9))) - .build() - ).name("tons_of_dirt").build(); - - final AdvancementTemplate tonsOfDirt = AdvancementTemplate.builder() - .criterion(tonsOfDirtCriterion) - .displayInfo(DisplayInfo.builder() - .icon(ItemTypes.DIRT) - .title(Component.text("Got tons of dirt!")) - .type(AdvancementTypes.CHALLENGE) - .hidden(true) - .build()) - .parent(lotsOfDirt) - .key(ResourceKey.of(this.plugin, "tons_of_dirt")) - .build(); - event.register(tonsOfDirt); - - this.counter1 = ScoreAdvancementCriterion.builder().goal(10).name("counter").build(); - this.counter1Bypass = AdvancementCriterion.dummy(); - AdvancementTemplate counterAdvancement1 = AdvancementTemplate.builder() - .criterion(OrCriterion.of(this.counter1, this.counter1Bypass)) - .displayInfo(DisplayInfo.builder() - .icon(ItemTypes.CHEST) - .title(Component.text("Open some chests.")) - .type(AdvancementTypes.GOAL) - .build()) - .parent(this.rootAdvancement) - .key(ResourceKey.of(this.plugin, "counting")) - .build(); - event.register(counterAdvancement1); - this.counterAdvancement1 = counterAdvancement1; - - this.counter2 = ScoreAdvancementCriterion.builder().goal(20).name("counter").build(); - AdvancementTemplate counterAdvancement2 = AdvancementTemplate.builder() - .criterion(this.counter2) - .displayInfo(DisplayInfo.builder() - .icon(ItemTypes.CHEST) - .title(Component.text("Open more chests")) - .type(AdvancementTypes.CHALLENGE) - .build()) - .parent(counterAdvancement1) - .key(ResourceKey.of(this.plugin, "counting_more")) - .build(); - event.register(counterAdvancement2); - this.counterAdvancement2 = counterAdvancement2; - - final AdvancementCriterion a = AdvancementCriterion.builder().name("A").build(); - final AdvancementCriterion b = AdvancementCriterion.builder().name("B").build(); - final AdvancementCriterion c = AdvancementCriterion.builder().name("C").build(); - final AdvancementCriterion d = AdvancementCriterion.builder().name("D").build(); - final AdvancementCriterion e = AdvancementCriterion.builder().name("E").build(); - final AdvancementCriterion f = AdvancementCriterion.builder().name("F").build(); - final AdvancementCriterion combinationCriterion = OrCriterion.of(a, AndCriterion.of(b, OrCriterion.of(c, d)), AndCriterion.of(e, f)); - final AdvancementTemplate combinationAdvancement = AdvancementTemplate.builder() - .criterion(combinationCriterion) - .displayInfo(DisplayInfo.builder() - .icon(ItemTypes.CHEST) - .title(Component.text("A || (B & (C || D)) || (E & F)")) - .description(Component.text("ABE ABF ACDE ACDF")) - .type(AdvancementTypes.CHALLENGE) - .build()) - .parent(counterAdvancement1) - .key(ResourceKey.of(this.plugin, "combination")) - .build(); - event.register(combinationAdvancement); + event.register(Triggers.Holder.TRIGGERS); + event.register(Advancements.Holder.ADVANCEMENTS); } class TriggerListeners { @Listener private void onContainerEvent(final ChangeInventoryEvent event, @First final ServerPlayer player) { - AdvancementTest.this.inventoryChangeTrigger.trigger(player); + Triggers.INVENTORY_CHANGE_TRIGGER.get().trigger(player); } @Listener private void onConainterEvent(final InteractContainerEvent.Open event, @First final ServerPlayer player) { - final AdvancementProgress progress1 = player.progress(AdvancementTest.this.counterAdvancement1); + final AdvancementProgress progress1 = player.progress(Advancements.COUNTER_1.get()); if (progress1.achieved()) { - final AdvancementProgress progress2 = player.progress(AdvancementTest.this.counterAdvancement2); - progress2.require(AdvancementTest.this.counter2).add(1); + final AdvancementProgress progress2 = player.progress(Advancements.COUNTER_2.get()); + progress2.require(AdvancementCriteria.COUNTER_2).add(1); } else { - progress1.require(AdvancementTest.this.counter1).add(1); + progress1.require(AdvancementCriteria.COUNTER_1).add(1); final Object carrier = ((CarriedInventory) event.container()).carrier().orElse(null); if (carrier instanceof BlockCarrier) { if (((BlockCarrier) carrier).location().blockType().isAnyOf(BlockTypes.TRAPPED_CHEST)) { - progress1.require(AdvancementTest.this.counter1Bypass).grant(); + progress1.require(AdvancementCriteria.COUNTER_1_BYPASS).grant(); } } } diff --git a/testplugins/src/main/java/org/spongepowered/test/chat/ChatTest.java b/testplugins/src/main/java/org/spongepowered/test/chat/ChatTest.java index b2f0b35a8ac..af7d582157f 100644 --- a/testplugins/src/main/java/org/spongepowered/test/chat/ChatTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/chat/ChatTest.java @@ -40,10 +40,8 @@ import org.apache.logging.log4j.Logger; import org.spongepowered.api.Game; import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.Server; import org.spongepowered.api.Sponge; -import org.spongepowered.api.adventure.ChatTypeTemplate; -import org.spongepowered.api.adventure.ChatTypes; +import org.spongepowered.api.adventure.ChatType; import org.spongepowered.api.adventure.ResolveOperations; import org.spongepowered.api.adventure.SpongeComponents; import org.spongepowered.api.command.Command; @@ -57,11 +55,14 @@ import org.spongepowered.api.event.filter.cause.Root; import org.spongepowered.api.event.lifecycle.ConstructPluginEvent; import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; -import org.spongepowered.api.event.lifecycle.StartedEngineEvent; +import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; import org.spongepowered.api.event.message.PlayerChatEvent; import org.spongepowered.api.event.network.ServerSideConnectionEvent; import org.spongepowered.api.item.ItemTypes; import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.registry.DefaultedRegistryReference; +import org.spongepowered.api.registry.RegistryRegistrationSet; +import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.util.locale.Locales; import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.builtin.jvm.Plugin; @@ -76,11 +77,26 @@ @Plugin("chattest") public class ChatTest implements LoadableModule { + public static final class ChatTypes { + + public static final class Holder { + + public static final RegistryRegistrationSet CHAT_TYPES = ChatTypes.BUILDER.build(); + } + + private static final RegistryRegistrationSet.Builder BUILDER = RegistryRegistrationSet.builder(RegistryTypes.CHAT_TYPE, Sponge::server); + + public static final DefaultedRegistryReference INVERTED_CHAT_ORDER = ChatTypes.BUILDER.register( + ResourceKey.of("chattest", "inverted"), + () -> ChatType.builder().translationKey("%s by <%s>") + .addContent().addSender() + .build()); + } + private static final Logger LOGGER = LogManager.getLogger(); private static final BossBar INFO_BAR = BossBar.bossBar(Component.translatable("chattest.bars.info"), 1f, BossBar.Color.PINK, BossBar.Overlay.PROGRESS); - public static final ResourceKey INVERTED_CHAT_ORDER = ResourceKey.of("chattest", "inverted"); private final Game game; private final PluginContainer container; @@ -102,14 +118,8 @@ private void constructed(final ConstructPluginEvent event) { } @Listener - private void onServerStarted(final StartedEngineEvent event) - { - // TODO register this earlier - static context? - final ChatTypeTemplate template = ChatTypeTemplate.builder().translationKey("%s by <%s>") - .addContent().addSender() - .key(INVERTED_CHAT_ORDER) - .build(); - Sponge.server().dataPackManager().save(template); + private void onRegisterRegistryValueEvent(final RegisterRegistryValueEvent event) { + event.register(ChatTypes.Holder.CHAT_TYPES); } @Override @@ -199,7 +209,7 @@ private void onChatDecorate(final PlayerChatEvent.Decorate event, final @Root Se @Listener private void onChatSubmit(final PlayerChatEvent.Submit event, final @Root ServerPlayer player) { - event.setChatType(ChatTypes.key(INVERTED_CHAT_ORDER)); + event.setChatType(ChatTypes.INVERTED_CHAT_ORDER); final Optional optPlayerName = event.player().flatMap(p -> p.get(Keys.DISPLAY_NAME)); final TextComponent name = Component.text("Prefix", NamedTextColor.RED) .append(Component.text(" | ", NamedTextColor.GOLD)) diff --git a/testplugins/src/main/java/org/spongepowered/test/command/raw/ClientSuggestionsRawCommand.java b/testplugins/src/main/java/org/spongepowered/test/command/raw/ClientSuggestionsRawCommand.java index 3a604ec6d66..c276276c9b6 100644 --- a/testplugins/src/main/java/org/spongepowered/test/command/raw/ClientSuggestionsRawCommand.java +++ b/testplugins/src/main/java/org/spongepowered/test/command/raw/ClientSuggestionsRawCommand.java @@ -36,6 +36,7 @@ import org.spongepowered.api.command.registrar.tree.CommandCompletionProviders; import org.spongepowered.api.command.registrar.tree.CommandTreeNode; import org.spongepowered.api.command.registrar.tree.CommandTreeNodeTypes; +import org.spongepowered.api.registry.RegistryHolder; import java.util.Collections; import java.util.List; @@ -76,11 +77,11 @@ public boolean canExecute(@NonNull final CommandCause cause) { } @Override - public CommandTreeNode.Root commandTree() { + public CommandTreeNode.Root commandTree(final RegistryHolder registryHolder) { return CommandTreeNode.root() - .child("s1", CommandTreeNodeTypes.RESOURCE_LOCATION.get().createNode() + .child("s1", CommandTreeNodeTypes.RESOURCE_LOCATION.get(registryHolder).createNode() .executable() - .completions(CommandCompletionProviders.ALL_RECIPES)); + .completions(CommandCompletionProviders.AVAILABLE_SOUNDS)); } } diff --git a/testplugins/src/main/java/org/spongepowered/test/command/raw/RawCommand.java b/testplugins/src/main/java/org/spongepowered/test/command/raw/RawCommand.java index 7d2f8c81815..54a7eb2e19b 100644 --- a/testplugins/src/main/java/org/spongepowered/test/command/raw/RawCommand.java +++ b/testplugins/src/main/java/org/spongepowered/test/command/raw/RawCommand.java @@ -35,6 +35,7 @@ import org.spongepowered.api.command.parameter.ArgumentReader; import org.spongepowered.api.command.registrar.tree.CommandTreeNode; import org.spongepowered.api.command.registrar.tree.CommandTreeNodeTypes; +import org.spongepowered.api.registry.RegistryHolder; import java.util.Arrays; import java.util.List; @@ -98,11 +99,11 @@ public Component usage(@NonNull final CommandCause cause) { } @Override - public CommandTreeNode.@NonNull Root commandTree() { - final CommandTreeNode.Argument firstStringKey = CommandTreeNodeTypes.STRING.get().createNode() + public CommandTreeNode.@NonNull Root commandTree(final RegistryHolder registryHolder) { + final CommandTreeNode.Argument firstStringKey = CommandTreeNodeTypes.STRING.get(registryHolder).createNode() .customCompletions().executable(); final CommandTreeNode.Argument secondStringKey = - CommandTreeNodeTypes.STRING.get().createNode().customCompletions().executable().redirect(firstStringKey); + CommandTreeNodeTypes.STRING.get(registryHolder).createNode().customCompletions().executable().redirect(firstStringKey); firstStringKey.child("s2", secondStringKey); return CommandTreeNode.root().executable().child("s1", firstStringKey); } diff --git a/testplugins/src/main/java/org/spongepowered/test/customregistry/CustomRegistryTest.java b/testplugins/src/main/java/org/spongepowered/test/customregistry/CustomRegistryTest.java index d548efe103c..7a60114589d 100644 --- a/testplugins/src/main/java/org/spongepowered/test/customregistry/CustomRegistryTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/customregistry/CustomRegistryTest.java @@ -29,16 +29,21 @@ import org.spongepowered.api.Server; import org.spongepowered.api.Sponge; import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.lifecycle.FreezeRegistryEvent; import org.spongepowered.api.event.lifecycle.RegisterRegistryEvent; import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; import org.spongepowered.api.event.lifecycle.StartingEngineEvent; +import org.spongepowered.api.item.ItemTypes; import org.spongepowered.api.registry.DefaultedRegistryReference; import org.spongepowered.api.registry.DefaultedRegistryType; import org.spongepowered.api.registry.RegistryKey; import org.spongepowered.api.registry.RegistryRoots; import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.plugin.builtin.jvm.Plugin; +import java.util.Map; + @Plugin("customregistrytest") public final class CustomRegistryTest { @@ -59,12 +64,15 @@ public final class CustomRegistryTest { @Listener public void onRegisterRegistryGameScoped(final RegisterRegistryEvent.GameScoped event) { - event.register(GAME_REG_KEY, true); + event.register(GAME_REG_KEY, true, (h) -> { + ItemTypes.BEDROCK.get(h); + return Map.of(); + }, RegistryTypes.ITEM_TYPE); } @Listener public void onRegisterRegistryValuesGameScoped(final RegisterRegistryValueEvent.GameScoped event) { - event.registry(GAME_CUSTOM_REGISTRY).register(GAME_CUSTOM_VALUE.location(), 10); + event.registry(GAME_CUSTOM_REGISTRY, (h, r) -> r.register(GAME_CUSTOM_VALUE.location(), 10), RegistryTypes.ITEM_TYPE); } @Listener @@ -74,7 +82,7 @@ public void onRegisterRegistryServerScoped(final RegisterRegistryEvent.EngineSco @Listener public void onRegisterRegistryValuesServerScoped(final RegisterRegistryValueEvent.EngineScoped event) { - event.registry(SERVER_CUSTOM_REGISTRY).register(SERVER_CUSTOM_VALUE.location(), 5); + event.registry(SERVER_CUSTOM_REGISTRY, r -> r.register(SERVER_CUSTOM_VALUE.location(), 5)); } @Listener @@ -84,7 +92,22 @@ public void onRegisterRegistryClientScoped(final RegisterRegistryEvent.EngineSco @Listener public void onRegisterRegistryValuesClientScoped(final RegisterRegistryValueEvent.EngineScoped event) { - event.registry(CLIENT_CUSTOM_REGISTRY).register(CLIENT_CUSTOM_VALUE.location(), 3); + event.registry(CLIENT_CUSTOM_REGISTRY, r -> r.register(CLIENT_CUSTOM_VALUE.location(), 3)); + } + + @Listener + public void onRegisterFreezeGameScoped(final FreezeRegistryEvent.Post.GameScoped event) { + final int gameValue = GAME_CUSTOM_VALUE.get(event.holder()); + } + + @Listener + public void onRegisterFreezeServerScoped(final FreezeRegistryEvent.Post.EngineScoped event) { + final int serverValue = SERVER_CUSTOM_VALUE.get(event.holder()); + } + + @Listener + public void onRegisterFreezeClientScoped(final FreezeRegistryEvent.Post.EngineScoped event) { + final int clientValue = CLIENT_CUSTOM_VALUE.get(event.holder()); } @Listener diff --git a/testplugins/src/main/java/org/spongepowered/test/damage/DamageTest.java b/testplugins/src/main/java/org/spongepowered/test/damage/DamageTest.java index e82b08214bf..e865f0a3037 100644 --- a/testplugins/src/main/java/org/spongepowered/test/damage/DamageTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/damage/DamageTest.java @@ -24,43 +24,77 @@ */ package org.spongepowered.test.damage; +import static net.kyori.adventure.text.Component.text; + import com.google.inject.Inject; import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; import org.spongepowered.api.command.Command; import org.spongepowered.api.command.CommandResult; import org.spongepowered.api.command.parameter.CommandContext; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.event.Listener; -import org.spongepowered.api.event.cause.entity.damage.DamageFunction; import org.spongepowered.api.event.cause.entity.damage.DamageModifier; import org.spongepowered.api.event.cause.entity.damage.DamageScalings; +import org.spongepowered.api.event.cause.entity.damage.DamageStep; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; import org.spongepowered.api.event.cause.entity.damage.DamageType; -import org.spongepowered.api.event.cause.entity.damage.DamageTypeTemplate; import org.spongepowered.api.event.cause.entity.damage.DamageTypes; import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; import org.spongepowered.api.event.entity.AttackEntityEvent; -import org.spongepowered.api.event.entity.DamageEntityEvent; +import org.spongepowered.api.event.entity.DamageCalculationEvent; import org.spongepowered.api.event.filter.cause.Root; import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; -import org.spongepowered.api.event.lifecycle.RegisterDataPackValueEvent; +import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; +import org.spongepowered.api.event.lifecycle.RegisterTagEvent; +import org.spongepowered.api.registry.DefaultedRegistryReference; import org.spongepowered.api.registry.RegistryKey; +import org.spongepowered.api.registry.RegistryRegistrationSet; import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.tag.DamageTypeTags; -import org.spongepowered.api.tag.TagTemplate; -import org.spongepowered.api.util.Tuple; import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.builtin.jvm.Plugin; import org.spongepowered.test.LoadableModule; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.OptionalDouble; @Plugin("damagetest") public class DamageTest implements LoadableModule { private static final ResourceKey EXHAUSTING_DAMAGE = ResourceKey.of("damagetest", "test"); + private static final DamageStepType START_BONUS = DamageStepType.create(); + private static final DamageStepType END_BONUS = DamageStepType.create(); + private static final DamageStepType DOUBLE_CRITICAL = DamageStepType.create(); + private static final DamageStepType DOUBLE_DOUBLE_CRITICAL = DamageStepType.create(); + + class Test { + + static final class Registry { + public static final RegistryRegistrationSet DAMAGE_TYPES = BUILDER.build(); + } + + private static final RegistryRegistrationSet.Builder BUILDER = RegistryRegistrationSet.builder(RegistryTypes.DAMAGE_TYPE, Sponge::server); + + public static final DefaultedRegistryReference EXHAUSTING_DAMAGE = BUILDER.register( + ResourceKey.of("damagetest", "test"), + () -> DamageType.builder().name("test").scaling(DamageScalings.NEVER.get()) + .exhaustion(100d) + .build()); + } + private final PluginContainer plugin; private final DamageListener listener = new DamageListener(); @@ -95,54 +129,119 @@ private void onRegisterSpongeCommand(final RegisterCommandEvent event) { - final DamageTypeTemplate template = DamageTypeTemplate.builder().name("test").scaling(DamageScalings.NEVER.get()) - .exhaustion(100d) - .key(EXHAUSTING_DAMAGE) - .build(); - event.register(template); + private void onRegisterRegistryValueEvent(final RegisterRegistryValueEvent event) { + event.register(Test.Registry.DAMAGE_TYPES); } @Listener - private void onDamageTypeTagPack(final RegisterDataPackValueEvent> event) { - final TagTemplate template = TagTemplate.builder(DataPacks.DAMAGE_TYPE_TAG).key(DamageTypeTags.BYPASSES_INVULNERABILITY.key()) - .addValue(RegistryKey.of(RegistryTypes.DAMAGE_TYPE, EXHAUSTING_DAMAGE)).build(); - event.register(template); + private void onRegisterTagEvent(final RegisterTagEvent event) { + event.tag(DamageTypeTags.BYPASSES_INVULNERABILITY).append(RegistryKey.of(RegistryTypes.DAMAGE_TYPE, EXHAUSTING_DAMAGE)); } + @Listener + public void onRegisterDamageStepType(final RegisterRegistryValueEvent.GameScoped event) { + event.registry(RegistryTypes.DAMAGE_STEP_TYPE, (r) -> { + r.register(ResourceKey.of(this.plugin, "start_bonus"), START_BONUS); + r.register(ResourceKey.of(this.plugin, "end_bonus"), END_BONUS); + r.register(ResourceKey.of(this.plugin, "double_critical"), DOUBLE_CRITICAL); + r.register(ResourceKey.of(this.plugin, "double_double_critical"), DOUBLE_DOUBLE_CRITICAL); + }); + } + + private static class CustomCause {} + private static class DamageListener { + private static final DecimalFormat decimalFormat = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.ROOT)); + + private static String format(final OptionalDouble value) { + return DamageListener.format(value.orElse(Double.NaN)); + } + + private static String format(final double value) { + return DamageListener.decimalFormat.format(value); + } + + @Listener + private void onAttackPre(final AttackEntityEvent.Pre event) { + event.addModifierBefore(DamageStepTypes.CRITICAL_HIT, + DamageModifier.builder().type(DOUBLE_CRITICAL).frameModifier((frame) -> frame.pushCause(new CustomCause())) + .damageFunction((step, damage) -> { + step.parent().get().skip(); + return damage * 2; + }).build()); + event.addModifierAfter(DOUBLE_CRITICAL, + DamageModifier.builder().type(DOUBLE_DOUBLE_CRITICAL).damageFunction((step, damage) -> damage * 2).build()); + } + + @Listener + private void onDamagePre(final DamageCalculationEvent.Pre event) { + event.addModifierBefore(DamageStepTypes.START, + DamageModifier.builder().type(START_BONUS).damageFunction((step, damage) -> damage + 1).build()); + + event.addModifierAfter(DamageStepTypes.END, + DamageModifier.builder().type(END_BONUS).damageFunction((step, damage) -> damage + 1).build()); + } + @Listener - private void onAttack(final AttackEntityEvent event, @Root DamageSource damageSource) { + private void onDamagePre(final DamageCalculationEvent.Pre event, @Root final DamageSource damageSource) { + final Component eventName = event instanceof AttackEntityEvent ? + text("AttackEntityEvent", NamedTextColor.RED) : text("DamageEntityEvent", NamedTextColor.BLUE); + final Audience audience = Sponge.server(); - audience.sendMessage(Component.text("------------AttackEntityEvent------------")); - audience.sendMessage(Component.text().content("entity: ").append(event.entity().displayName().get()).build()); - audience.sendMessage(Component.text("damage type: " + damageSource.type().key(RegistryTypes.DAMAGE_TYPE))); - audience.sendMessage(Component.text("damage: " + event.originalDamage())); - audience.sendMessage(Component.text("modifiers:")); - for (final DamageFunction f : event.originalFunctions()) { - final DamageModifier modifier = f.modifier(); - final Tuple tuple = event.originalModifierDamage(modifier); - audience.sendMessage(Component.text(" " + ResourceKey.resolve(modifier.group()).value() + "/" + modifier.type().key(RegistryTypes.DAMAGE_MODIFIER_TYPE).value() + ": " + tuple.first() + " -> " + tuple.second())); - } - audience.sendMessage(Component.text("final damage: " + event.originalFinalDamage())); - audience.sendMessage(Component.text("-----------------------------------------")); + audience.sendMessage(text().content("-------------").append(eventName, text(".Pre", NamedTextColor.YELLOW), text("---------------"))); + audience.sendMessage(text().content(damageSource.type().key(RegistryTypes.DAMAGE_TYPE).value()) + .color(NamedTextColor.GOLD).append(text(" → ", NamedTextColor.WHITE), event.entity().displayName().get())); + audience.sendMessage(text("base damage: " + format(event.baseDamage()))); + audience.sendMessage(text("-----------------------------------------------")); } @Listener - private void onDamage(final DamageEntityEvent event, @Root DamageSource damageSource) { + private void onDamagePost(final DamageCalculationEvent.Post event, @Root final DamageSource damageSource) { + final Component eventName = event instanceof AttackEntityEvent ? + text("AttackEntityEvent", NamedTextColor.RED) : text("DamageEntityEvent", NamedTextColor.BLUE); + final Audience audience = Sponge.server(); - audience.sendMessage(Component.text("------------DamageEntityEvent------------")); - audience.sendMessage(Component.text().content("entity: ").append(event.entity().displayName().get()).build()); - audience.sendMessage(Component.text("damage type: " + damageSource.type().key(RegistryTypes.DAMAGE_TYPE))); - audience.sendMessage(Component.text("damage: " + event.originalDamage())); - audience.sendMessage(Component.text("modifiers:")); - for (final DamageFunction f : event.originalFunctions()) { - final DamageModifier modifier = f.modifier(); - final Tuple tuple = event.originalModifierDamage(modifier); - audience.sendMessage(Component.text(" " + ResourceKey.resolve(modifier.group()).value() + "/" + modifier.type().key(RegistryTypes.DAMAGE_MODIFIER_TYPE).value() + ": " + tuple.first() + " -> " + tuple.second())); + audience.sendMessage(text().content("-------------").append(eventName, text(".Post", NamedTextColor.GREEN), text("--------------"))); + audience.sendMessage(text().content(damageSource.type().key(RegistryTypes.DAMAGE_TYPE).value()) + .color(NamedTextColor.GOLD).append(text(" → ", NamedTextColor.WHITE), event.entity().displayName().get())); + audience.sendMessage(text("base damage: " + format(event.baseDamage()))); + audience.sendMessage(text("steps:")); + for (final DamageStep step : event.history().rootSteps()) { + audience.sendMessage(formatStep(step)); + } + audience.sendMessage(text("final damage: " + format(event.finalDamage()))); + audience.sendMessage(text("-----------------------------------------------")); + } + + private static Component formatStep(final DamageStep step) { + final List components = new ArrayList<>(); + components.add(Component.text(format(step.damageBeforeChildren()))); + formatStep(step, components); + return Component.join(JoinConfiguration.separator(Component.text(" → ", NamedTextColor.GRAY)), components); + } + + private static void formatStep(final DamageStep step, final List components) { + for (final DamageStep child : step.childrenBefore()) { + formatStep(child, components); + } + + final List causeClasses = new ArrayList<>(); + for (final Object obj : step.cause().all()) { + causeClasses.add(obj.getClass().getSimpleName()); + } + + components.add(Component.text() + .content(step.type().findKey(RegistryTypes.DAMAGE_STEP_TYPE).map(Key::value).orElse("?")) + .color(step.parent().isEmpty() ? NamedTextColor.AQUA : NamedTextColor.YELLOW) + .decoration(TextDecoration.STRIKETHROUGH, step.isSkipped()) + .hoverEvent(HoverEvent.showText(Component.text(String.join(" ", causeClasses)))) + .build()); + + components.add(Component.text(format(step.damageAfterSelf()))); + + for (final DamageStep child : step.childrenAfter()) { + formatStep(child, components); } - audience.sendMessage(Component.text("final damage: " + event.originalFinalDamage())); - audience.sendMessage(Component.text("-----------------------------------------")); } } } diff --git a/testplugins/src/main/java/org/spongepowered/test/data/AttributeDataTest.java b/testplugins/src/main/java/org/spongepowered/test/data/AttributeDataTest.java new file mode 100644 index 00000000000..ce27e065d2c --- /dev/null +++ b/testplugins/src/main/java/org/spongepowered/test/data/AttributeDataTest.java @@ -0,0 +1,124 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.test.data; + +import com.google.inject.Inject; +import net.kyori.adventure.text.Component; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.command.Command; +import org.spongepowered.api.command.CommandResult; +import org.spongepowered.api.command.parameter.CommandContext; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.entity.attribute.AttributeModifier; +import org.spongepowered.api.entity.attribute.AttributeOperations; +import org.spongepowered.api.entity.attribute.ItemAttribute; +import org.spongepowered.api.entity.attribute.type.AttributeTypes; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.equipment.EquipmentConditions; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.builtin.jvm.Plugin; +import org.spongepowered.test.LoadableModule; + +@Plugin("attributedata") +public class AttributeDataTest implements LoadableModule { + + private final PluginContainer plugin; + + @Inject + public AttributeDataTest(final PluginContainer plugin) { + this.plugin = plugin; + } + + @Override + public void enable(final CommandContext ctx) { + } + + @Listener + public void onRegisterCommand(final RegisterCommandEvent event) { + final Command.Builder builder = Command.builder(); + builder.addChild(Command.builder().executor(this::items).build(), "items"); + event.register(this.plugin, builder.build(), "attributedata"); + } + + private CommandResult items(final CommandContext commandContext) { + final ServerPlayer player = commandContext.cause().first(ServerPlayer.class).orElse(null); + if (player == null) { + return CommandResult.error(Component.text("Must be run ingame by a player")); + } + + final ItemStack stack1 = ItemStack.of(ItemTypes.DIAMOND_BOOTS); + stack1.offer(Keys.CUSTOM_NAME, Component.text("Speed when on feet, jump boost when in hand")); + stack1.offerSingle(Keys.ITEM_ATTRIBUTES, ItemAttribute.of( + AttributeTypes.MOVEMENT_SPEED, + AttributeModifier.of(ResourceKey.of(this.plugin, "boots_speed"), AttributeOperations.ADDITION, 1), + EquipmentConditions.FEET + )); + stack1.offerSingle(Keys.ITEM_ATTRIBUTES, ItemAttribute.of( + AttributeTypes.JUMP_STRENGTH, + AttributeModifier.of(ResourceKey.of(this.plugin, "boots_jump"), AttributeOperations.ADDITION, 1), + EquipmentConditions.HAND + )); + + final ItemStack stack2 = ItemStack.of(ItemTypes.NETHERITE_SWORD); + stack2.offer(Keys.CUSTOM_NAME, Component.text("Sword without damage")); + stack2.getValue(Keys.ITEM_ATTRIBUTES) + .map(Value::asMutable) + .map(v -> v.transform(list -> list.stream() + .filter(attribute -> attribute.type() != AttributeTypes.ATTACK_DAMAGE.get()) + .toList())) + .ifPresent(stack2::offer); + + // In vanilla only one modifier with the same + // key is applied within each attribute type. + // This is to ensure that vanilla accepts + // modifiers with the same key on the item. + final ItemStack stack3 = ItemStack.of(ItemTypes.SUGAR); + final ResourceKey sameKey = ResourceKey.of(this.plugin, "same_key"); + stack3.offer(Keys.CUSTOM_NAME, Component.text("Scale and one (out of 2) health boost")); + stack3.offerSingle(Keys.ITEM_ATTRIBUTES, ItemAttribute.of( + AttributeTypes.MAX_HEALTH, + AttributeModifier.of(sameKey, AttributeOperations.ADDITION, 10), + EquipmentConditions.MAINHAND + )); + stack3.offerSingle(Keys.ITEM_ATTRIBUTES, ItemAttribute.of( + AttributeTypes.MAX_HEALTH, + AttributeModifier.of(sameKey, AttributeOperations.ADDITION, 20), + EquipmentConditions.MAINHAND + )).ifNotSuccessful(RuntimeException::new); + stack3.offerSingle(Keys.ITEM_ATTRIBUTES, ItemAttribute.of( + AttributeTypes.SCALE, + AttributeModifier.of(sameKey, AttributeOperations.MULTIPLY_TOTAL, 1), + EquipmentConditions.MAINHAND + )); + + player.inventory().offer(stack1, stack2, stack3); + return CommandResult.success(); + } +} diff --git a/testplugins/src/main/java/org/spongepowered/test/data/ConsumableDataTest.java b/testplugins/src/main/java/org/spongepowered/test/data/ConsumableDataTest.java new file mode 100644 index 00000000000..e5bb59a3d58 --- /dev/null +++ b/testplugins/src/main/java/org/spongepowered/test/data/ConsumableDataTest.java @@ -0,0 +1,129 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.test.data; + +import com.google.inject.Inject; +import net.kyori.adventure.text.Component; +import org.spongepowered.api.command.Command; +import org.spongepowered.api.command.CommandResult; +import org.spongepowered.api.command.parameter.CommandContext; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.api.effect.potion.PotionEffect; +import org.spongepowered.api.effect.potion.PotionEffectTypes; +import org.spongepowered.api.effect.sound.SoundTypes; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.util.Ticks; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.builtin.jvm.Plugin; +import org.spongepowered.test.LoadableModule; + +@Plugin("consumabledata") +public class ConsumableDataTest implements LoadableModule { + + private final PluginContainer plugin; + + @Inject + public ConsumableDataTest(final PluginContainer plugin) { + this.plugin = plugin; + } + + @Override + public void enable(final CommandContext ctx) { + } + + @Listener + public void onRegisterCommand(final RegisterCommandEvent event) { + final Command.Builder builder = Command.builder(); + builder.addChild(Command.builder().executor(this::items).build(), "items"); + builder.addChild(Command.builder().executor(this::applyTeleport).build(), "applyteleport"); + builder.addChild(Command.builder().executor(this::applySound).build(), "applysound"); + event.register(this.plugin, builder.build(), "consumabledata"); + } + + private CommandResult items(final CommandContext commandContext) { + final ServerPlayer player = commandContext.cause().first(ServerPlayer.class).orElse(null); + if (player == null) { + return CommandResult.error(Component.text("Must be run ingame by a player")); + } + + final ItemStack stack1 = ItemStack.of(ItemTypes.BRICKS); + stack1.offer(Keys.CUSTOM_NAME, Component.text("Remove slowness, get speed & regeneration")); + stack1.offerSingle(Keys.CONSUME_ACTIONS, ItemAction.playSound(SoundTypes.BLOCK_ANVIL_BREAK)); + stack1.offerSingle(Keys.CONSUME_ACTIONS, ItemAction.teleportRandomly(5)); + stack1.offerSingle(Keys.CONSUME_ACTIONS, ItemAction.removeEffects(PotionEffectTypes.SLOWNESS)); + stack1.offerSingle(Keys.CONSUME_ACTIONS, ItemAction.applyEffects( + PotionEffect.of(PotionEffectTypes.SPEED, 1, Ticks.of(20 * 30)), + PotionEffect.of(PotionEffectTypes.REGENERATION, 0, Ticks.of(20 * 30)) + )); + + final ItemStack stack2 = ItemStack.of(ItemTypes.CHORUS_FRUIT); + stack2.offer(Keys.CUSTOM_NAME, Component.text("Remove all effects, get slowness & haste with 50% chance")); + stack2.offerSingle(Keys.CONSUME_ACTIONS, ItemAction.clearEffects()); + stack2.offerSingle(Keys.CONSUME_ACTIONS, ItemAction.applyEffects(0.5, + PotionEffect.of(PotionEffectTypes.SLOWNESS, 1, Ticks.of(20 * 10)), + PotionEffect.of(PotionEffectTypes.HASTE, 0, Ticks.of(20 * 10)) + )); + + final ItemStack stack3 = ItemStack.of(ItemTypes.TOTEM_OF_UNDYING); + stack3.offer(Keys.CUSTOM_NAME, Component.text("No death protection")); + stack3.remove(Keys.DEATH_PROTECTION_ACTIONS); + + final ItemStack stack4 = ItemStack.of(ItemTypes.ANVIL); + stack4.offer(Keys.CUSTOM_NAME, Component.text("Teleport on death")); + stack4.offerSingle(Keys.DEATH_PROTECTION_ACTIONS, ItemAction.teleportRandomly(20)); + + final ItemStack stack5 = ItemStack.of(ItemTypes.POTION); + stack5.offer(Keys.CUSTOM_NAME, Component.text("Hide potion effects")); + stack5.offer(Keys.HIDE_POTION_EFFECTS, true); + + player.inventory().offer(stack1, stack2, stack3, stack4, stack5); + return CommandResult.success(); + } + + private CommandResult applyTeleport(final CommandContext commandContext) { + final ServerPlayer player = commandContext.cause().first(ServerPlayer.class).orElse(null); + if (player == null) { + return CommandResult.error(Component.text("Must be run ingame by a player")); + } + + ItemAction.teleportRandomly(20).apply(player); + return CommandResult.success(); + } + + private CommandResult applySound(final CommandContext commandContext) { + final ServerPlayer player = commandContext.cause().first(ServerPlayer.class).orElse(null); + if (player == null) { + return CommandResult.error(Component.text("Must be run ingame by a player")); + } + + ItemAction.playSound(SoundTypes.BLOCK_BELL_USE).apply(player); + return CommandResult.success(); + } +} diff --git a/testplugins/src/main/java/org/spongepowered/test/data/InfiniteTicksDataTest.java b/testplugins/src/main/java/org/spongepowered/test/data/InfiniteTicksDataTest.java index 29dfbfa6ae1..75f5d5a5b52 100644 --- a/testplugins/src/main/java/org/spongepowered/test/data/InfiniteTicksDataTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/data/InfiniteTicksDataTest.java @@ -34,7 +34,6 @@ import org.spongepowered.api.Sponge; import org.spongepowered.api.command.Command; import org.spongepowered.api.command.CommandResult; -import org.spongepowered.api.command.exception.CommandException; import org.spongepowered.api.command.parameter.CommandContext; import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.command.parameter.managed.ValueParser; @@ -56,11 +55,10 @@ import org.spongepowered.api.event.entity.living.player.CooldownEvent; import org.spongepowered.api.event.item.inventory.UseItemStackEvent; import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; -import org.spongepowered.api.event.lifecycle.RegisterDataPackValueEvent; +import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; import org.spongepowered.api.event.world.ChangeWeatherEvent; import org.spongepowered.api.item.ItemType; import org.spongepowered.api.item.ItemTypes; -import org.spongepowered.api.item.recipe.RecipeRegistration; import org.spongepowered.api.item.recipe.RecipeType; import org.spongepowered.api.item.recipe.RecipeTypes; import org.spongepowered.api.item.recipe.cooking.CookingRecipe; @@ -136,23 +134,15 @@ public InfiniteTicksDataTest(final PluginContainer plugin) { @Override public void enable(final CommandContext ctx) { this.enabled = true; - try { - Sponge.server().commandManager().process("reload"); - } catch (final CommandException e) { - e.printStackTrace(); - } Sponge.eventManager().registerListeners(this.plugin, new InfiniteTicksDataTest.Listeners()); + Sponge.server().dataPackManager().reload(); } @Override public void disable(final CommandContext ctx) { this.enabled = false; - try { - Sponge.server().commandManager().process("reload"); - } catch (final CommandException e) { - e.printStackTrace(); - } - Sponge.eventManager().registerListeners(this.plugin, new InfiniteTicksDataTest.Listeners()); + Sponge.eventManager().unregisterListeners(this.plugin); + Sponge.server().dataPackManager().reload(); } @Listener @@ -169,6 +159,7 @@ private void onRegisterCommand(final RegisterCommandEvent final Parameter.Value potionEffectParameter = Parameter.registryElement(TypeToken.get(PotionEffectType.class), RegistryTypes.POTION_EFFECT_TYPE, "minecraft").key("potionEffectType").build(); final Parameter.Value itemTypeParameter = Parameter.registryElement(TypeToken.get(ItemType.class), RegistryTypes.ITEM_TYPE, "minecraft").key("itemType").build(); final Parameter.Value weatherTypeParameter = Parameter.registryElement(TypeToken.get(WeatherType.class), RegistryTypes.WEATHER_TYPE, "sponge").key("weatherType").build(); + final Parameter.Value resourceKeyParameter = Parameter.resourceKey().key("resourceKey").build(); event.register(this.plugin, Command.builder() .addParameter(entityParameter) @@ -250,11 +241,11 @@ private void onRegisterCommand(final RegisterCommandEvent }).build(), "offerInfiniteRangedAttackDelay"); event.register(this.plugin, Command.builder() - .addParameter(itemTypeParameter) + .addParameter(resourceKeyParameter) .executor(context -> { final ServerPlayer player = context.cause().first(ServerPlayer.class).get(); - final ItemType itemType = context.requireOne(itemTypeParameter); - if (player.cooldownTracker().setCooldown(itemType, Ticks.infinite())) { + final ResourceKey group = context.requireOne(resourceKeyParameter); + if (player.cooldownTracker().setCooldown(group, Ticks.infinite())) { context.sendMessage(Component.text("Applied!", NamedTextColor.GREEN)); return CommandResult.success(); } else { @@ -324,24 +315,25 @@ private void onRegisterCommand(final RegisterCommandEvent } @Listener - private void onRecipeRegistry(final RegisterDataPackValueEvent event) { + private void onRecipeRegistry(final RegisterRegistryValueEvent event) { if (!this.enabled) { return; } - event.register(this.createCookingRecipe(RecipeTypes.BLASTING.get(), "infinite_blasting_sponge")); - event.register(this.createCookingRecipe(RecipeTypes.CAMPFIRE_COOKING.get(), "infinite_campire_cooking_sponge")); - event.register(this.createCookingRecipe(RecipeTypes.SMELTING.get(), "infinite_smelting_sponge")); - event.register(this.createCookingRecipe(RecipeTypes.SMOKING.get(), "infinite_smoking_sponge")); + event.registry(RegistryTypes.RECIPE, registry -> { + registry.register(ResourceKey.of(this.plugin, "infinite_blasting_sponge"), this.createCookingRecipe(RecipeTypes.BLASTING.get())); + registry.register(ResourceKey.of(this.plugin, "infinite_campire_cooking_sponge"), this.createCookingRecipe(RecipeTypes.CAMPFIRE_COOKING.get())); + registry.register(ResourceKey.of(this.plugin, "infinite_smelting_sponge"), this.createCookingRecipe(RecipeTypes.SMELTING.get())); + registry.register(ResourceKey.of(this.plugin, "infinite_smoking_sponge"), this.createCookingRecipe(RecipeTypes.SMOKING.get())); + }); } - private RecipeRegistration createCookingRecipe(final RecipeType type, final String key) { + private CookingRecipe createCookingRecipe(final RecipeType type) { return CookingRecipe.builder() .type(type) .ingredient(ItemTypes.SPONGE.get()) .result(ItemTypes.WET_SPONGE.get()) .cookingTime(Ticks.infinite()) - .key(ResourceKey.of(this.plugin, key)) .build(); } diff --git a/testplugins/src/main/java/org/spongepowered/test/entity/EntityTest.java b/testplugins/src/main/java/org/spongepowered/test/entity/EntityTest.java index e8a0a1c2338..2e4874b7541 100644 --- a/testplugins/src/main/java/org/spongepowered/test/entity/EntityTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/entity/EntityTest.java @@ -37,7 +37,6 @@ import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.config.ConfigDir; import org.spongepowered.api.data.type.HandTypes; -import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.EntityType; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.event.Listener; @@ -101,7 +100,7 @@ public void onRegisterCommand(final RegisterCommandEvent event) { }) .build(), "testload"); final Parameter.Value> entityTypeParam = - Parameter.registryElement(new TypeToken>() {}, RegistryTypes.ENTITY_TYPE, "minecraft").key("entityType").build(); + Parameter.registryElement(new TypeToken>() {}, RegistryTypes.ENTITY_TYPE, "minecraft").key("entityType").build(); event.register(this.plugin, Command.builder() .addChild(Command.builder() .addParameter(entityTypeParam) diff --git a/testplugins/src/main/java/org/spongepowered/test/map/MapTest.java b/testplugins/src/main/java/org/spongepowered/test/map/MapTest.java index cc69c8f3bcc..c618bdfeb28 100644 --- a/testplugins/src/main/java/org/spongepowered/test/map/MapTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/map/MapTest.java @@ -510,7 +510,7 @@ private CommandResult setMapNether(final CommandContext ctx) throws CommandExcep } final Optional nether = Sponge.server().worldManager().world(DefaultWorldKeys.THE_NETHER); if (!nether.isPresent()) { - final CompletableFuture loadedNether = Sponge.server().worldManager().loadWorld(DefaultWorldKeys.THE_NETHER); + final CompletableFuture> loadedNether = Sponge.server().worldManager().loadWorld(DefaultWorldKeys.THE_NETHER); loadedNether.whenComplete((v, e) -> { if (e != null) { audience.sendMessage(Component.text("Failed to load nether world!", NamedTextColor.GREEN)); diff --git a/testplugins/src/main/java/org/spongepowered/test/movement/MovementTest.java b/testplugins/src/main/java/org/spongepowered/test/movement/MovementTest.java index 73524955ac0..95d3460cb66 100644 --- a/testplugins/src/main/java/org/spongepowered/test/movement/MovementTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/movement/MovementTest.java @@ -193,6 +193,9 @@ public void onMovePlayer(final MoveEntityEvent event) { final Optional type = event.context().get(EventContextKeys.MOVEMENT_TYPE); final Logger logger = MovementTest.this.plugin.logger(); logger.info(MovementTest.marker, name + ": Movement Type: " + type.map(t -> RegistryTypes.MOVEMENT_TYPE.get().valueKey(t).toString()).orElse("?")); + if (type.isEmpty()) { + Thread.dumpStack(); + } if (MovementTest.this.cancelMovement) { event.setCancelled(true); diff --git a/testplugins/src/main/java/org/spongepowered/test/portal/PortalTest.java b/testplugins/src/main/java/org/spongepowered/test/portal/PortalTest.java index 4d46920fa4f..8aab6784590 100644 --- a/testplugins/src/main/java/org/spongepowered/test/portal/PortalTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/portal/PortalTest.java @@ -48,12 +48,24 @@ import org.spongepowered.plugin.builtin.jvm.Plugin; import org.spongepowered.test.LoadableModule; +import java.lang.invoke.MethodHandles; import java.util.Optional; @Plugin("portaltest") public final class PortalTest implements LoadableModule { public final static class Holder { + private static final MethodHandles.Lookup HOLDER_LOOKUP = MethodHandles.publicLookup(); + private static final MethodHandles.Lookup PORTAL_TEST_LOOKUP; + + static { + try { + PORTAL_TEST_LOOKUP = MethodHandles.privateLookupIn(PortalTest.class, HOLDER_LOOKUP); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + public final static PortalTestListener INSTANCE = new PortalTestListener(); } @@ -66,7 +78,7 @@ public PortalTest(final PluginContainer pluginContainer) { @Override public void enable(final CommandContext ctx) { - Sponge.eventManager().registerListeners(this.pluginContainer, PortalTest.Holder.INSTANCE); + Sponge.eventManager().registerListeners(this.pluginContainer, PortalTest.Holder.INSTANCE, PortalTest.Holder.PORTAL_TEST_LOOKUP); ctx.cause().first(ServerPlayer.class).ifPresent(player -> { final var portalONE = PortalLogic.builder().addSimplePortal((from, fromPos, entity) -> Optional.of(entity.serverLocation().add(Vector3i.ONE))).build(); player.offer(Keys.PORTAL_LOGIC, portalONE); diff --git a/testplugins/src/main/java/org/spongepowered/test/projectile/ProjectileTest.java b/testplugins/src/main/java/org/spongepowered/test/projectile/ProjectileTest.java index 9d9743c1ce3..019a0155124 100644 --- a/testplugins/src/main/java/org/spongepowered/test/projectile/ProjectileTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/projectile/ProjectileTest.java @@ -79,7 +79,7 @@ public void enable(final CommandContext ctx) { private void registerCommand(final RegisterCommandEvent event) { final Parameter.Value> entityTypeParameter = Parameter.registryElement( - new TypeToken>() {}, + new TypeToken>() {}, (ctx) -> Sponge.game(), RegistryTypes.ENTITY_TYPE, "minecraft", @@ -201,14 +201,15 @@ public ProjectileTestListener() { this.projectileTypes.add(EntityTypes.WITHER_SKULL.get()); this.projectileTypes.add(EntityTypes.EYE_OF_ENDER.get()); // this.projectileTypes.add(EntityTypes.FISHING_BOBBER.get()); - this.projectileTypes.add(EntityTypes.POTION.get()); + this.projectileTypes.add(EntityTypes.SPLASH_POTION.get()); + this.projectileTypes.add(EntityTypes.LINGERING_POTION.get()); this.projectileTypes.add(EntityTypes.LLAMA_SPIT.get()); this.projectileTypes.add(EntityTypes.DRAGON_FIREBALL.get()); this.projectileTypes.add(EntityTypes.SHULKER_BULLET.get()); } @Listener - private void onClickBlock(final InteractBlockEvent.Secondary event, @First final ServerPlayer player) { + private void onClickBlock(final InteractBlockEvent.Secondary.Pre event, @First final ServerPlayer player) { final Vector3d interactionPoint = event.interactionPoint(); final ServerWorld world = player.world(); final EntityType nextType = this.projectileTypes.poll(); diff --git a/testplugins/src/main/java/org/spongepowered/test/recipe/RecipeTest.java b/testplugins/src/main/java/org/spongepowered/test/recipe/RecipeTest.java index 2fb84f4a299..6c16f49adbe 100644 --- a/testplugins/src/main/java/org/spongepowered/test/recipe/RecipeTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/recipe/RecipeTest.java @@ -30,7 +30,6 @@ import net.kyori.adventure.text.format.NamedTextColor; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; -import org.spongepowered.api.command.exception.CommandException; import org.spongepowered.api.command.parameter.CommandContext; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.meta.BannerPatternLayer; @@ -38,13 +37,13 @@ import org.spongepowered.api.data.type.DyeColor; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.event.Listener; -import org.spongepowered.api.event.lifecycle.RegisterDataPackValueEvent; +import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; import org.spongepowered.api.item.ItemType; import org.spongepowered.api.item.ItemTypes; import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.ItemStackLike; import org.spongepowered.api.item.inventory.Slot; import org.spongepowered.api.item.inventory.query.QueryTypes; -import org.spongepowered.api.item.recipe.RecipeRegistration; import org.spongepowered.api.item.recipe.RecipeTypes; import org.spongepowered.api.item.recipe.cooking.CookingRecipe; import org.spongepowered.api.item.recipe.crafting.CraftingRecipe; @@ -81,192 +80,168 @@ public RecipeTest(final PluginContainer plugin) { @Override public void enable(final CommandContext ctx) { this.enabled = true; - try { - Sponge.server().commandManager().process("reload"); - } catch (final CommandException e) { - e.printStackTrace(); - } + Sponge.server().dataPackManager().reload(); } @Override public void disable(final CommandContext ctx) { this.enabled = false; - try { - Sponge.server().commandManager().process("reload"); - } catch (final CommandException e) { - e.printStackTrace(); - } + Sponge.server().dataPackManager().reload(); } @SuppressWarnings("unchecked") @Listener - private void onRecipeRegistry(final RegisterDataPackValueEvent event) { + private void onRecipeRegistry(final RegisterRegistryValueEvent event) { if (!this.enabled) { return; } // Standard recipes and ItemStack(with nbt) ingredient and results + event.registry(RegistryTypes.RECIPE, registry -> { + final Ingredient whiteRock = Ingredient.of(ItemTypes.POLISHED_DIORITE.get()); + final Ingredient whiteBed = Ingredient.of(ItemTypes.WHITE_BED.get()); + final ItemStack bedrock = ItemStack.of(ItemTypes.BEDROCK); - final Ingredient whiteRock = Ingredient.of(ItemTypes.POLISHED_DIORITE.get()); - final Ingredient whiteBed = Ingredient.of(ItemTypes.WHITE_BED.get()); - final ItemStack bedrock = ItemStack.of(ItemTypes.BEDROCK); - - final RecipeRegistration whiteBedrockRecipe = CraftingRecipe.shapedBuilder().rows() + final CraftingRecipe whiteBedrockRecipe = CraftingRecipe.shapedBuilder().rows() .row(whiteRock, whiteRock, whiteRock) .row(whiteRock, whiteBed, whiteRock) .row(whiteRock, whiteRock, whiteRock) .result(bedrock.copy()) - .key(ResourceKey.of(this.plugin, "white_bedrock")) .build(); - event.register(whiteBedrockRecipe); + registry.register(ResourceKey.of(this.plugin, "white_bedrock"), whiteBedrockRecipe); - final Ingredient redRock = Ingredient.of(ItemTypes.POLISHED_GRANITE); - final Ingredient redBed = Ingredient.of(ItemTypes.RED_BED); - final ItemStack redBedRock = bedrock.copy(); - redBedRock.offer(Keys.CUSTOM_NAME, Component.text("Bedrock", NamedTextColor.RED)); + final Ingredient redRock = Ingredient.of(ItemTypes.POLISHED_GRANITE); + final Ingredient redBed = Ingredient.of(ItemTypes.RED_BED); + final ItemStack redBedRock = bedrock.copy(); + redBedRock.offer(Keys.CUSTOM_NAME, Component.text("Bedrock", NamedTextColor.RED)); - final RecipeRegistration redBedrockRecipe = CraftingRecipe.shapedBuilder().rows() + final CraftingRecipe redBedrockRecipe = CraftingRecipe.shapedBuilder().rows() .aisle("ggg", "gbg", "ggg") .where('g', redRock) .where('b', redBed) .result(redBedRock) - .key(ResourceKey.of(this.plugin, "red_bedrock")) .build(); - event.register(redBedrockRecipe); + registry.register(ResourceKey.of(this.plugin, "red_bedrock"), redBedrockRecipe); - final ItemStack moreBedrock = bedrock.copy(); - moreBedrock.setQuantity(9); - final RecipeRegistration moreBedrockRecipe = CraftingRecipe.shapedBuilder().rows() + final ItemStack moreBedrock = bedrock.copy(); + moreBedrock.setQuantity(9); + final CraftingRecipe moreBedrockRecipe = CraftingRecipe.shapedBuilder().rows() .aisle("ggg", "gbg", "ggg") .where('g', redRock) .where('b', Ingredient.of(bedrock.copy())) .result(moreBedrock) - .key(ResourceKey.of(this.plugin, "more_red_bedrock")) .build(); - event.register(moreBedrockRecipe); + registry.register(ResourceKey.of(this.plugin, "more_red_bedrock"), moreBedrockRecipe); - final RecipeRegistration cheapGoldenAppleRecipe = CraftingRecipe.shapelessBuilder() + final CraftingRecipe cheapGoldenAppleRecipe = CraftingRecipe.shapelessBuilder() .addIngredients(ItemTypes.YELLOW_WOOL, ItemTypes.APPLE) .result(ItemStack.of(ItemTypes.GOLDEN_APPLE)) - .key(ResourceKey.of(this.plugin, "cheap_golden_apple")) .build(); - event.register(cheapGoldenAppleRecipe); + registry.register(ResourceKey.of(this.plugin, "cheap_golden_apple"), cheapGoldenAppleRecipe); - final RecipeRegistration expensiveGoldenAppleRecipe = CraftingRecipe.shapelessBuilder() + final CraftingRecipe expensiveGoldenAppleRecipe = CraftingRecipe.shapelessBuilder() .addIngredients(ItemTypes.YELLOW_WOOL, ItemTypes.ENCHANTED_GOLDEN_APPLE) .result(ItemStack.of(ItemTypes.GOLDEN_APPLE)) - .key(ResourceKey.of(this.plugin, "expensive_golden_apple")) .build(); - event.register(expensiveGoldenAppleRecipe); + registry.register(ResourceKey.of(this.plugin, "expensive_golden_apple"), expensiveGoldenAppleRecipe); - final Ingredient bedrocks = Ingredient.of(bedrock, redBedRock); - final RecipeRegistration bedrocksToGranite = CraftingRecipe.shapelessBuilder() + final Ingredient bedrocks = Ingredient.of(bedrock, redBedRock); + final CraftingRecipe bedrocksToGranite = CraftingRecipe.shapelessBuilder() .addIngredients(bedrocks, bedrocks) .result(ItemStack.of(ItemTypes.GRANITE, 13)) - .key(ResourceKey.of(this.plugin, "bedrocks_to_granite")) .build(); - event.register(bedrocksToGranite); + registry.register(ResourceKey.of(this.plugin, "bedrocks_to_granite"), bedrocksToGranite); - final RecipeRegistration diamondToCoalRecipe = CookingRecipe.builder().type(RecipeTypes.SMELTING) + final CookingRecipe diamondToCoalRecipe = CookingRecipe.builder().type(RecipeTypes.SMELTING) .ingredient(Ingredient.of(ItemTypes.DIAMOND)) .result(ItemTypes.COAL) .experience(0) - .key(ResourceKey.of(this.plugin, "diamond_to_coal")) .build(); - event.register(diamondToCoalRecipe); + registry.register(ResourceKey.of(this.plugin, "diamond_to_coal"), diamondToCoalRecipe); - final RecipeRegistration burnPaperAndSticksRecipe = CookingRecipe.builder().type(RecipeTypes.SMELTING) + final CookingRecipe burnPaperAndSticksRecipe = CookingRecipe.builder().type(RecipeTypes.SMELTING) .ingredient(Ingredient.of(ItemTypes.PAPER, ItemTypes.STICK)) .result(ItemTypes.GUNPOWDER) .experience(1) .cookingTime(Ticks.of(1)) - .key(ResourceKey.of(this.plugin, "burn_paper_and_sticks")) .build(); - event.register(burnPaperAndSticksRecipe); + registry.register(ResourceKey.of(this.plugin, "burn_paper_and_sticks"), burnPaperAndSticksRecipe); - final RecipeRegistration charcoalToCoalRecipe = CookingRecipe.builder().type(RecipeTypes.BLASTING) + final CookingRecipe charcoalToCoalRecipe = CookingRecipe.builder().type(RecipeTypes.BLASTING) .ingredient(Ingredient.of(ItemTypes.CHARCOAL)) .result(ItemTypes.COAL) - .key(ResourceKey.of(this.plugin, "charcoal_to_coal")) .build(); - event.register(charcoalToCoalRecipe); + registry.register(ResourceKey.of(this.plugin, "charcoal_to_coal"), charcoalToCoalRecipe); - final ItemStack redderBedrock = bedrock.copy(); - redderBedrock.offer(Keys.CUSTOM_NAME, Component.text("Bedrock", NamedTextColor.DARK_RED)); + final ItemStack redderBedrock = bedrock.copy(); + redderBedrock.offer(Keys.CUSTOM_NAME, Component.text("Bedrock", NamedTextColor.DARK_RED)); - final RecipeRegistration removeRedOnBedrock = CookingRecipe.builder().type(RecipeTypes.BLASTING) + final CookingRecipe removeRedOnBedrock = CookingRecipe.builder().type(RecipeTypes.BLASTING) .ingredient(Ingredient.of(redBedRock)) .result(redderBedrock) .cookingTime(Ticks.of(20)) .experience(100) - .key(ResourceKey.of(this.plugin, "redder_bedrock")) .build(); - event.register(removeRedOnBedrock); + registry.register(ResourceKey.of(this.plugin, "redder_bedrock"), removeRedOnBedrock); - final RecipeRegistration overcookedPorkchopRecipe = CookingRecipe.builder().type(RecipeTypes.SMOKING) + final CookingRecipe overcookedPorkchopRecipe = CookingRecipe.builder().type(RecipeTypes.SMOKING) .ingredient(Ingredient.of(ItemTypes.COOKED_PORKCHOP)) .result(ItemTypes.COAL) - .key(ResourceKey.of(this.plugin, "overcooked_porkchop")) .build(); - event.register(overcookedPorkchopRecipe); + registry.register(ResourceKey.of(this.plugin, "overcooked_porkchop"), overcookedPorkchopRecipe); - final RecipeRegistration sticksToTorches = CookingRecipe.builder().type(RecipeTypes.CAMPFIRE_COOKING) + final CookingRecipe sticksToTorches = CookingRecipe.builder().type(RecipeTypes.CAMPFIRE_COOKING) .ingredient(Ingredient.of(ItemTypes.STICK)) .result(ItemTypes.TORCH) .cookingTime(Ticks.of(20)) - .key(ResourceKey.of(this.plugin, "stick_to_torch")) .build(); - event.register(sticksToTorches); + registry.register(ResourceKey.of(this.plugin, "stick_to_torch"), sticksToTorches); - final RecipeRegistration stonecutter1 = StoneCutterRecipe.builder() + final StoneCutterRecipe stonecutter1 = StoneCutterRecipe.builder() .ingredient(ItemTypes.BEDROCK) .result(ItemStack.of(ItemTypes.BLACK_CONCRETE, 32)) - .key(ResourceKey.of(this.plugin, "cut_bedrock_to_concrete")) .build(); - final RecipeRegistration stonecutter2 = StoneCutterRecipe.builder() + final StoneCutterRecipe stonecutter2 = StoneCutterRecipe.builder() .ingredient(ItemTypes.BEDROCK) .result(ItemStack.of(ItemTypes.BLACK_GLAZED_TERRACOTTA, 16)) - .key(ResourceKey.of(this.plugin, "cut_bedrock_to_terracotta")) .build(); - final RecipeRegistration stonecutter3 = StoneCutterRecipe.builder() + final StoneCutterRecipe stonecutter3 = StoneCutterRecipe.builder() .ingredient(ItemTypes.BEDROCK) .result(ItemStack.of(ItemTypes.BLACK_WOOL, 64)) - .key(ResourceKey.of(this.plugin, "cut_bedrock_wool")) .build(); - event.register(stonecutter1); - event.register(stonecutter2); - event.register(stonecutter3); + registry.register(ResourceKey.of(this.plugin, "cut_bedrock_to_concrete"), stonecutter1); + registry.register(ResourceKey.of(this.plugin, "cut_bedrock_to_terracotta"), stonecutter2); + registry.register(ResourceKey.of(this.plugin, "cut_bedrock_wool"), stonecutter3); - // Predicate Ingredients + // Predicate Ingredients - final Predicate hardnessPredicate = stack -> stack.type().block().map(b -> b.defaultState().get(Keys.DESTROY_SPEED).orElse(0d) > 20).orElse(false); // e.g. obsidian - final Ingredient hardBlock = Ingredient.of(ResourceKey.of(this.plugin, "hardblock"), hardnessPredicate, ItemStack.of(ItemTypes.OBSIDIAN)); - final RecipeRegistration hardblockToWool = + final Predicate hardnessPredicate = stack -> stack.type().block().map(b -> b.defaultState().get(Keys.DESTROY_SPEED).orElse(0d) > 20).orElse(false); // e.g. obsidian + final Ingredient hardBlock = Ingredient.of(ResourceKey.of(this.plugin, "hardblock"), hardnessPredicate, ItemStack.of(ItemTypes.OBSIDIAN)); + final CraftingRecipe hardblockToWool = ShapelessCraftingRecipe.builder().addIngredients(hardBlock).result(ItemStack.of(ItemTypes.WHITE_WOOL)) - .key(ResourceKey.of(this.plugin, "hardblock_to_wool")) - .build(); + .build(); - event.register(hardblockToWool); + registry.register(ResourceKey.of(this.plugin, "hardblock_to_wool"), hardblockToWool); - // Function Results + // Function Results - final ItemStack villagerEgg = ItemStack.of(ItemTypes.VILLAGER_SPAWN_EGG); - final RecipeRegistration villagerSpawnEggRecipe = ShapedCraftingRecipe.builder() + final ItemStack villagerEgg = ItemStack.of(ItemTypes.VILLAGER_SPAWN_EGG); + final CraftingRecipe villagerSpawnEggRecipe = ShapedCraftingRecipe.builder() .aisle(" e ", "eve", " e ") .where('v', Ingredient.of(ItemTypes.BOOK)) .where('e', Ingredient.of(ItemTypes.EMERALD_BLOCK)) @@ -276,15 +251,14 @@ private void onRecipeRegistry(final RegisterDataPackValueEvent player = Sponge.server().causeStackManager().currentCause().first(ServerPlayer.class); final Optional biome = player.map(p -> p.world().biome(p.blockPosition())); final String name = biome.map(present -> RegistryTypes.BIOME.keyFor(player.get().world(), present)).map(ResourceKey::toString).orElse("Unknown"); - final Integer biomeTemperature = biome.map(Biome::temperature).map(d -> (int) (d*10)).orElse(0); - final Integer biomeHumidity = biome.map(Biome::humidity).map(d -> (int) (d*10)).orElse(0); + final Integer biomeTemperature = biome.map(Biome::temperature).map(d -> (int) (d * 10)).orElse(0); + final Integer biomeHumidity = biome.map(Biome::humidity).map(d -> (int) (d * 10)).orElse(0); final TextComponent temperature = Component.text("Temperature: ").append(Component.text(biomeTemperature)); final TextComponent humidity = Component.text("Humidity: ").append(Component.text(biomeHumidity)); writtenBook.offer(Keys.CUSTOM_NAME, Component.text("Biome Data: " + name)); @@ -301,33 +275,30 @@ private void onRecipeRegistry(final RegisterDataPackValueEvent { final int blacks = grid.query(QueryTypes.ITEM_TYPE, ItemTypes.BLACK_WOOL).capacity(); final int whites = grid.query(QueryTypes.ITEM_TYPE, ItemTypes.WHITE_WOOL).capacity(); return blacks > whites ? ItemStack.of(ItemTypes.BLACK_WOOL, 3) : ItemStack.of(ItemTypes.WHITE_WOOL, 3); }, ItemStack.of(ItemTypes.GRAY_WOOL)) - .key(ResourceKey.of(this.plugin, "black_or_white")) .build(); - event.register(blackOrWhiteRecipe); + registry.register(ResourceKey.of(this.plugin, "black_or_white"), blackOrWhiteRecipe); - // Custom results dont work well in cooking recipes - final ItemStack anvil = ItemStack.of(ItemTypes.DAMAGED_ANVIL); - final RecipeRegistration cookedAnvilRecipe = CookingRecipe.builder().type(RecipeTypes.BLASTING) + // Custom results dont work well in cooking recipes + final ItemStack anvil = ItemStack.of(ItemTypes.DAMAGED_ANVIL); + final CookingRecipe cookedAnvilRecipe = CookingRecipe.builder().type(RecipeTypes.BLASTING) .ingredient(ItemTypes.IRON_BLOCK) .result(inv -> anvil.copy(), anvil.copy()) - .key(ResourceKey.of(this.plugin, "cooked_anvil")) .build(); - event.register(cookedAnvilRecipe); + registry.register(ResourceKey.of(this.plugin, "cooked_anvil"), cookedAnvilRecipe); - final RecipeRegistration cutPlanksRecipe = StoneCutterRecipe.builder() + final StoneCutterRecipe cutPlanksRecipe = StoneCutterRecipe.builder() .ingredient(ItemTypes.OAK_PLANKS) .result(input -> { if (new Random().nextBoolean()) { @@ -335,11 +306,10 @@ private void onRecipeRegistry(final RegisterDataPackValueEvent { if (inv.capacity() != 9) { return false; @@ -377,47 +347,43 @@ private void onRecipeRegistry(final RegisterDataPackValueEvent inv.slots().stream().map(Slot::peek) - .map(item -> (item.type().isAnyOf(ItemTypes.WET_SPONGE) ? ItemTypes.SPONGE : ItemTypes.AIR).get()) - .map(ItemStack::of) - .collect(Collectors.toList())) + .map(item -> (item.type().isAnyOf(ItemTypes.WET_SPONGE) ? ItemTypes.SPONGE : ItemTypes.AIR).get()) + .map(ItemStack::of) + .collect(Collectors.toList())) .result(ItemStack.of(ItemTypes.WATER_BUCKET)) - .key(ResourceKey.of(this.plugin, "squeeze_sponge_recipe")) .build(); - event.register(squeezeSpongeRecipe); + registry.register(ResourceKey.of(this.plugin, "squeeze_sponge_recipe"), squeezeSpongeRecipe); - // Smithing recipes + // Smithing recipes - final RecipeRegistration ironToGoldIngot = SmithingRecipe.builder() + final SmithingRecipe ironToGoldIngot = SmithingRecipe.builder() .template(ItemTypes.PAPER) .base(ItemTypes.IRON_INGOT) .addition(ItemTypes.NETHERITE_INGOT) .result(ItemStack.of(ItemTypes.GOLD_INGOT)) - .key(ResourceKey.of(this.plugin, "iron_to_gold_ingot")) .build(); - event.register(ironToGoldIngot); + registry.register(ResourceKey.of(this.plugin, "iron_to_gold_ingot"), ironToGoldIngot); - final ItemStack chainMail = ItemStack.of(ItemTypes.CHAINMAIL_CHESTPLATE); - chainMail.offer(Keys.CUSTOM_NAME, Component.text("Heavy Chainmail", NamedTextColor.DARK_GRAY)); - chainMail.offer(Keys.LORE, Arrays.asList(Component.text("Smithing occured", NamedTextColor.DARK_GRAY))); - final RecipeRegistration smithChainmail = SmithingRecipe.builder() + final ItemStack chainMail = ItemStack.of(ItemTypes.CHAINMAIL_CHESTPLATE); + chainMail.offer(Keys.CUSTOM_NAME, Component.text("Heavy Chainmail", NamedTextColor.DARK_GRAY)); + chainMail.offer(Keys.LORE, Arrays.asList(Component.text("Smithing occured", NamedTextColor.DARK_GRAY))); + final SmithingRecipe smithChainmail = SmithingRecipe.builder() .template(ItemTypes.PAPER) .base(ItemTypes.IRON_CHESTPLATE) .addition(ItemTypes.FIRE_CHARGE) .result(chainMail) - .key(ResourceKey.of(this.plugin, "smith_chainmail")) .build(); - event.register(smithChainmail); - + registry.register(ResourceKey.of(this.plugin, "smith_chainmail"), smithChainmail); + }); } } diff --git a/testplugins/src/main/java/org/spongepowered/test/tag/TagTest.java b/testplugins/src/main/java/org/spongepowered/test/tag/TagTest.java index fef2244b238..bf61e68731c 100644 --- a/testplugins/src/main/java/org/spongepowered/test/tag/TagTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/tag/TagTest.java @@ -42,12 +42,11 @@ import org.spongepowered.api.command.parameter.managed.ValueParameter; import org.spongepowered.api.command.parameter.managed.standard.VariableValueParameters; import org.spongepowered.api.data.type.HandTypes; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.entity.EntityType; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; -import org.spongepowered.api.event.lifecycle.RegisterDataPackValueEvent; +import org.spongepowered.api.event.lifecycle.RegisterTagEvent; import org.spongepowered.api.fluid.FluidType; import org.spongepowered.api.item.ItemType; import org.spongepowered.api.item.ItemTypes; @@ -57,7 +56,6 @@ import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.tag.BlockTypeTags; import org.spongepowered.api.tag.Tag; -import org.spongepowered.api.tag.TagTemplate; import org.spongepowered.api.tag.Taggable; import org.spongepowered.api.util.blockray.RayTrace; import org.spongepowered.api.world.LocatableBlock; @@ -86,89 +84,56 @@ public TagTest(final PluginContainer pluginContainer, final Logger logger) { private static final TypeToken> FLUID_TYPE_TAG_TOKEN = new TypeToken>() {}; @Listener - private void registerTags(final RegisterDataPackValueEvent<@NonNull TagTemplate> event) { + private void registerTags(final RegisterTagEvent event) { this.logger.info("Adding tags."); - final TagTemplate tagRegistration = TagTemplate.builder(DataPacks.BLOCK_TAG) - .key(ResourceKey.of(this.pluginContainer, "wool")) - .addValue(BlockTypes.SHORT_GRASS) - .build(); - - event.register(tagRegistration); - - final TagTemplate woolLog = TagTemplate.builder(DataPacks.BLOCK_TAG) - .key(BlockTypeTags.WOOL.key()) - .addValue(BlockTypes.OAK_LOG) - .build(); - - event.register(woolLog); - - final TagTemplate woolGrass = TagTemplate.builder(DataPacks.BLOCK_TAG) - .key(ResourceKey.minecraft("wool")) - .addValue(BlockTypes.GRASS_BLOCK) - .build(); - - event.register(woolGrass); - - final TagTemplate underwaterDiamond = TagTemplate.builder(DataPacks.BLOCK_TAG) - .key(BlockTypeTags.UNDERWATER_BONEMEALS.key()) - .addValue(BlockTypes.DIAMOND_BLOCK) - .build(); - - event.register(underwaterDiamond); - - final TagTemplate ores = TagTemplate.builder(DataPacks.BLOCK_TAG) - .key(ResourceKey.of(this.pluginContainer, "ores")) - .addValue(BlockTypes.COAL_ORE) - .addValue(BlockTypes.IRON_ORE) - .addValue(BlockTypes.LAPIS_ORE) - .addValue(BlockTypes.REDSTONE_ORE) - .addValue(BlockTypes.EMERALD_ORE) - .addValue(BlockTypes.DIAMOND_ORE) - .addValue(BlockTypes.NETHER_QUARTZ_ORE) - .addChild(BlockTypeTags.GOLD_ORES) // Test gold ore child. - .build(); - - event.register(ores); - - final TagTemplate oresAndBlocks = TagTemplate.builder(DataPacks.BLOCK_TAG) - .key(ResourceKey.of(this.pluginContainer, "oresandblocks")) - .addValue(BlockTypes.COAL_BLOCK) - .addValue(BlockTypes.IRON_BLOCK) - .addValue(BlockTypes.LAPIS_BLOCK) - .addValue(BlockTypes.REDSTONE_BLOCK) - .addValue(BlockTypes.GOLD_BLOCK) - .addValue(BlockTypes.EMERALD_BLOCK) - .addValue(BlockTypes.DIAMOND_BLOCK) - .addValue(BlockTypes.QUARTZ_BLOCK) - .addChild(ores) // Test child TagTemplate - .build(); - - event.register(oresAndBlocks); + event.tag(Tag.of(RegistryTypes.BLOCK_TYPE, ResourceKey.of(this.pluginContainer, "wool"))) + .append(BlockTypes.SHORT_GRASS); + + event.tag(BlockTypeTags.WOOL).append(BlockTypes.OAK_LOG); + + event.tag(Tag.of(RegistryTypes.BLOCK_TYPE, ResourceKey.minecraft("wool"))) + .append(BlockTypes.GRASS_BLOCK); + + event.tag(BlockTypeTags.UNDERWATER_BONEMEALS).append(BlockTypes.DIAMOND_BLOCK); + + final Tag ores = Tag.of(RegistryTypes.BLOCK_TYPE, ResourceKey.of(this.pluginContainer, "ores")); + + event.tag(ores) + .append(BlockTypes.COAL_ORE) + .append(BlockTypes.IRON_ORE) + .append(BlockTypes.LAPIS_ORE) + .append(BlockTypes.REDSTONE_ORE) + .append(BlockTypes.EMERALD_ORE) + .append(BlockTypes.DIAMOND_ORE) + .append(BlockTypes.NETHER_QUARTZ_ORE) + .append(BlockTypeTags.GOLD_ORES) // Test gold ore child. + ; + + event.tag(Tag.of(RegistryTypes.BLOCK_TYPE, ResourceKey.of(this.pluginContainer, "oresandblocks"))) + .append(BlockTypes.COAL_BLOCK) + .append(BlockTypes.IRON_BLOCK) + .append(BlockTypes.LAPIS_BLOCK) + .append(BlockTypes.REDSTONE_BLOCK) + .append(BlockTypes.GOLD_BLOCK) + .append(BlockTypes.EMERALD_BLOCK) + .append(BlockTypes.DIAMOND_BLOCK) + .append(BlockTypes.QUARTZ_BLOCK) + .append(ores) // Test child TagTemplate + ; final ResourceKey nonExistentKey = ResourceKey.of("notrealnamespace", "notrealvalue"); - final TagTemplate brokenChildTag = TagTemplate.builder(DataPacks.ITEM_TAG) - .key(ResourceKey.of(this.pluginContainer, "brokenchildtag")) - .addChild(Tag.of(RegistryTypes.ITEM_TYPE, nonExistentKey), true) - .build(); - - event.register(brokenChildTag); - final TagTemplate brokenValueTag = TagTemplate.builder(DataPacks.ITEM_TAG) - .key(ResourceKey.of(this.pluginContainer, "brokenvaluetag")) - .addValue(RegistryKey.of(RegistryTypes.ITEM_TYPE, nonExistentKey)) - .build(); + event.tag(Tag.of(RegistryTypes.ITEM_TYPE, ResourceKey.of(this.pluginContainer, "brokenchildtag"))) + .append(Tag.of(RegistryTypes.ITEM_TYPE, nonExistentKey)); - event.register(brokenValueTag); - - final TagTemplate stillWorkingTag = TagTemplate.builder(DataPacks.ITEM_TAG) - .key(ResourceKey.of(this.pluginContainer, "stillworkingtag")) - .addValue(RegistryKey.of(RegistryTypes.ITEM_TYPE, nonExistentKey), false) - .addChild(Tag.of(RegistryTypes.ITEM_TYPE, nonExistentKey), false) - .addValue(ItemTypes.REDSTONE) - .build(); + event.tag(Tag.of(RegistryTypes.ITEM_TYPE, ResourceKey.of(this.pluginContainer, "brokenvaluetag"))) + .append(RegistryKey.of(RegistryTypes.ITEM_TYPE, nonExistentKey)); - event.register(stillWorkingTag); + event.tag(Tag.of(RegistryTypes.ITEM_TYPE, ResourceKey.of(this.pluginContainer, "stillworkingtag"))) + .append(RegistryKey.of(RegistryTypes.ITEM_TYPE, nonExistentKey)) + .append(Tag.of(RegistryTypes.ITEM_TYPE, nonExistentKey)) + .append(ItemTypes.REDSTONE); } @Listener @@ -179,7 +144,7 @@ private void registerCommands(final RegisterCommandEvent .executor(ctx -> { final BlockType blockType = TagTest.raytraceBlock(ctx).blockState().type(); final Audience audience = ctx.cause().audience(); - TagTest.sendTags(audience, blockType); + TagTest.sendTags(audience, blockType, RegistryTypes.BLOCK_TYPE); return CommandResult.success(); }) .build(); @@ -188,7 +153,7 @@ private void registerCommands(final RegisterCommandEvent .executor(ctx -> { final ItemType itemType = TagTest.requireItemInHand(ctx); final Audience audience = ctx.cause().audience(); - TagTest.sendTags(audience, itemType); + TagTest.sendTags(audience, itemType, RegistryTypes.ITEM_TYPE); return CommandResult.success(); }) .build(); @@ -197,7 +162,7 @@ private void registerCommands(final RegisterCommandEvent .executor(ctx -> { final EntityType entityType = TagTest.raytraceEntity(ctx); final Audience audience = ctx.cause().audience(); - TagTest.sendTags(audience, entityType); + TagTest.sendTags(audience, entityType, RegistryTypes.ENTITY_TYPE); return CommandResult.success(); }) .build(); @@ -206,7 +171,7 @@ private void registerCommands(final RegisterCommandEvent .executor(ctx -> { final FluidType fluidType = TagTest.raytraceBlock(ctx).blockState().fluidState().type(); final Audience audience = ctx.cause().audience(); - TagTest.sendTags(audience, fluidType); + TagTest.sendTags(audience, fluidType, RegistryTypes.FLUID_TYPE); return CommandResult.success(); }) .build(); @@ -330,9 +295,9 @@ private static ItemType requireItemInHand(final CommandContext ctx) throws Comma return heldItem.type(); } - private static > void sendTags(final Audience audience, final T taggable) { - final Collection> tags = taggable.tags(); - final String taggableKey = taggable.registryType().get().valueKey(taggable).toString(); + private static > void sendTags(final Audience audience, final T taggable, final DefaultedRegistryType registry) { + final Collection> tags = taggable.tags(registry).toList(); + final String taggableKey = taggable.key(registry).toString(); if (tags.isEmpty()) { audience.sendMessage(Component.text(taggableKey + " has no tags", NamedTextColor.RED)); return; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/equipment/EquipmentInventoryMixin_ItemStackLike.java b/testplugins/src/main/java/org/spongepowered/test/viewer/ViewerTest.java similarity index 56% rename from src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/equipment/EquipmentInventoryMixin_ItemStackLike.java rename to testplugins/src/main/java/org/spongepowered/test/viewer/ViewerTest.java index 87611017b43..99943c3dd25 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/itemstacklike/item/inventory/equipment/EquipmentInventoryMixin_ItemStackLike.java +++ b/testplugins/src/main/java/org/spongepowered/test/viewer/ViewerTest.java @@ -22,24 +22,34 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.itemstacklike.item.inventory.equipment; +package org.spongepowered.test.viewer; +import com.google.inject.Inject; +import org.spongepowered.api.command.Command; +import org.spongepowered.api.command.CommandResult; +import org.spongepowered.api.effect.Viewer; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; +import org.spongepowered.api.item.ItemTypes; import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.equipment.EquipmentInventory; -import org.spongepowered.api.item.inventory.equipment.EquipmentType; -import org.spongepowered.api.item.inventory.transaction.InventoryTransactionResult; -import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.builtin.jvm.Plugin; -import java.util.function.Supplier; +@Plugin("viewertest") +public final class ViewerTest { -@Mixin(value = EquipmentInventory.class, remap = false) -public interface EquipmentInventoryMixin_ItemStackLike { + private final PluginContainer plugin; - default InventoryTransactionResult set(EquipmentType equipmentType, ItemStack stack) { - return ((EquipmentInventory) this).set(equipmentType, stack); + @Inject + public ViewerTest(final PluginContainer plugin) { + this.plugin = plugin; } - default InventoryTransactionResult set(Supplier equipmentType, ItemStack stack) { - return ((EquipmentInventory) this).set(equipmentType, stack); + @Listener + public void onRegisterCommand(final RegisterCommandEvent event) { + event.register(this.plugin, Command.builder().executor(context -> { + context.cause().first(Viewer.class).get().playTotemOfUndyingEffect(ItemStack.of(ItemTypes.REDSTONE)); + return CommandResult.success(); + }).build(), "totem"); } } diff --git a/testplugins/src/main/java/org/spongepowered/test/volumestream/VolumeStreamTest.java b/testplugins/src/main/java/org/spongepowered/test/volumestream/VolumeStreamTest.java index 1de067274ed..67e0a85ccb0 100644 --- a/testplugins/src/main/java/org/spongepowered/test/volumestream/VolumeStreamTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/volumestream/VolumeStreamTest.java @@ -407,7 +407,7 @@ private void onInteract(final InteractBlockEvent.Primary event, @Root final Play } @Listener - public void onInteract(final InteractBlockEvent.Secondary event, @Root final Player player) { + public void onInteract(final InteractBlockEvent.Secondary.Pre event, @Root final Player player) { event.context().get(EventContextKeys.USED_ITEM).ifPresent(snapshot -> { final BlockSnapshot block = event.block(); if (snapshot.type().equals(ItemTypes.WOODEN_AXE.get()) && block != BlockSnapshot.empty()) { diff --git a/testplugins/src/main/java/org/spongepowered/test/world/WorldTest.java b/testplugins/src/main/java/org/spongepowered/test/world/WorldTest.java index 358e3364690..3c8768dd0bd 100644 --- a/testplugins/src/main/java/org/spongepowered/test/world/WorldTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/world/WorldTest.java @@ -38,29 +38,27 @@ import org.spongepowered.api.command.parameter.CommandContext; import org.spongepowered.api.command.parameter.CommonParameters; import org.spongepowered.api.command.parameter.Parameter; -import org.spongepowered.api.data.Keys; -import org.spongepowered.api.data.persistence.DataContainer; import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.util.Axis; import org.spongepowered.api.world.WorldType; -import org.spongepowered.api.world.WorldTypeTemplate; import org.spongepowered.api.world.WorldTypes; +import org.spongepowered.api.world.generation.ChunkGenerator; import org.spongepowered.api.world.portal.PortalLogic; -import org.spongepowered.api.world.server.DataPackManager; import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; -import org.spongepowered.api.world.server.WorldTemplate; +import org.spongepowered.api.world.server.WorldArchetype; +import org.spongepowered.api.world.server.WorldArchetypeType; +import org.spongepowered.api.world.server.WorldArchetypeTypes; +import org.spongepowered.api.world.server.storage.ServerWorldProperties; import org.spongepowered.math.vector.Vector3d; import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.builtin.jvm.Plugin; import java.io.IOException; -import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; @@ -163,7 +161,7 @@ private CommandResult loadWorld(final CommandContext context, final Parameter.Va if (t != null) { context.cause().audience().sendMessage(Identity.nil(), Component.text(t.getMessage())); } else { - if (r != null) { + if (r.isPresent()) { context.cause().audience().sendMessage(Identity.nil(), Component.text("World loaded successfully!")); } else { context.cause().audience().sendMessage(Identity.nil(), Component.text("World failed to load!")); @@ -177,17 +175,12 @@ private CommandResult loadWorld(final CommandContext context, final Parameter.Va private CommandResult createWorld(final CommandContext context,final Parameter.Value worldKeyParameter, final Parameter.Value worldTypeParameter) { final ResourceKey key = context.requireOne(worldKeyParameter); final WorldType worldType = context.requireOne(worldTypeParameter); - final WorldTemplate template = WorldTemplate.builder() - .from(WorldTemplate.overworld()) - .key(key) - .add(Keys.WORLD_TYPE, worldType) - .add(Keys.PERFORM_SPAWN_LOGIC, true) - .build(); - - this.game.server().worldManager().loadWorld(template).whenComplete((r, t) -> { + final WorldArchetypeType worldArchetypeType = WorldArchetypeType.of(worldType, ChunkGenerator.overworld()); + + this.game.server().worldManager().loadWorld(key, ServerWorldProperties.LoadOptions.create(WorldArchetype.of(worldArchetypeType))).whenComplete((r, t) -> { if (t != null) { context.cause().audience().sendMessage(Identity.nil(), Component.text(t.getMessage())); - } else if (r != null) { + } else if (r.isPresent()) { context.cause().audience().sendMessage(Identity.nil(), Component.text("World created successfully!")); } else { context.cause().audience().sendMessage(Identity.nil(), Component.text("World failed to create!")); @@ -281,39 +274,29 @@ private CommandResult whereami(final CommandContext context, final Parameter.Val private CommandResult worldTypes(final CommandContext commandContext) { - final Optional optPlayer = commandContext.cause().first(ServerPlayer.class); for (final WorldType wt : WorldTypes.registry().stream().toList()) { - final WorldTypeTemplate template = WorldTypeTemplate.builder().fromValue(wt).key(ResourceKey.of(this.plugin, "test")).build(); - final DataContainer dataContainer = template.toContainer(); - optPlayer.ifPresent(player -> player.sendMessage(Component.text(template.key().toString()))); - System.out.println(template.key()); - try { - System.out.println(DataFormats.JSON.get().write(dataContainer)); - final WorldTypeTemplate rebuiltTemplate = WorldTypeTemplate.builder().fromDataPack(dataContainer) - .key(ResourceKey.of(this.plugin, "custom" + template.key().value())).build(); - } catch (final IOException e) { - e.printStackTrace(); - } + final ResourceKey key = wt.key(RegistryTypes.WORLD_TYPE); + wt.toDataPack(Sponge.server()).ifPresentOrElse(d -> { + try { + System.out.println(String.format("%s: ", DataFormats.JSON.get().write(d))); + } catch (IOException e) { + e.printStackTrace(); + } + }, () -> System.out.println(String.format("%s: Is unserializable", key))); } return CommandResult.success(); } private CommandResult worldTemplates(final CommandContext commandContext) { - final DataPackManager dm = Sponge.server().dataPackManager(); - final List templates = dm.list(DataPacks.WORLD); - for (final ResourceKey key : templates) { - - try { - final Optional template = dm.load(DataPacks.WORLD, key).join(); - if (template.isPresent()) { - System.out.println(key + DataFormats.JSON.get().write(template.get().toContainer())); - } else { - System.out.println(key + " (no template)"); + for (final WorldArchetypeType archetypeType : WorldArchetypeTypes.registry().stream().toList()) { + final ResourceKey key = archetypeType.key(RegistryTypes.WORLD_ARCHETYPE_TYPE); + archetypeType.toDataPack(Sponge.server()).ifPresentOrElse(d -> { + try { + System.out.println(String.format("%s: ", DataFormats.JSON.get().write(d))); + } catch (IOException e) { + e.printStackTrace(); } - } catch (final Exception e) { - System.err.println(key + " " + e.getMessage()); - } - + }, () -> System.out.println(String.format("%s: Is unserializable", key))); } return CommandResult.success(); } diff --git a/testplugins/src/main/java/org/spongepowered/test/worldgen/BiomeTest.java b/testplugins/src/main/java/org/spongepowered/test/worldgen/BiomeTest.java index e338b45d318..91f785a1e9b 100644 --- a/testplugins/src/main/java/org/spongepowered/test/worldgen/BiomeTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/worldgen/BiomeTest.java @@ -35,15 +35,13 @@ import org.spongepowered.api.command.parameter.CommandContext; import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.data.Keys; -import org.spongepowered.api.datapack.DataPackTypes; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.effect.particle.ParticleTypes; import org.spongepowered.api.effect.sound.SoundTypes; import org.spongepowered.api.entity.EntityCategories; +import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; import org.spongepowered.api.registry.Registry; import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.world.biome.Biome; -import org.spongepowered.api.world.biome.BiomeTemplate; import org.spongepowered.api.world.biome.Biomes; import org.spongepowered.api.world.biome.ambient.ParticleConfig; import org.spongepowered.api.world.biome.ambient.SoundConfig; @@ -51,7 +49,6 @@ import org.spongepowered.api.world.generation.feature.DecorationSteps; import org.spongepowered.api.world.generation.feature.FeatureType; import org.spongepowered.api.world.generation.feature.PlacedFeatures; -import org.spongepowered.api.world.server.DataPackManager; import java.util.Arrays; import java.util.Collections; @@ -79,14 +76,12 @@ private CommandResult info(final CommandContext ctx, final Parameter.Value { - if (!list.isEmpty()) { - ctx.sendMessage(Identity.nil(), Component.text("Carvers Step:", NamedTextColor.DARK_AQUA)); - list.forEach(configuredCarver -> { - ctx.sendMessage(Identity.nil(), Component.text(" - " + configuredCarver.type().getClass().getSimpleName(), NamedTextColor.GRAY)); - }); - } - }); + if (!biome.carvers().isEmpty()) { + ctx.sendMessage(Identity.nil(), Component.text("Carvers Step:", NamedTextColor.DARK_AQUA)); + biome.carvers().forEach(configuredCarver -> { + ctx.sendMessage(Identity.nil(), Component.text(" - " + configuredCarver.type().getClass().getSimpleName(), NamedTextColor.GRAY)); + }); + } biome.ambientMood().ifPresent(mood -> { ctx.sendMessage(Identity.nil(), Component.text("Mood: " + mood, NamedTextColor.DARK_AQUA)); @@ -104,28 +99,26 @@ private CommandResult info(final CommandContext ctx, final Parameter.Value naturalSpawners = defaultBiome.spawners().get(EntityCategories.MONSTER.get()); - Collections.shuffle(naturalSpawners); - final List spawner = Arrays.asList(naturalSpawners.get(0)); + public void register(final RegisterRegistryValueEvent event) { + event.registry(RegistryTypes.BIOME, (h, s) -> { + final Biome defaultBiome = Biomes.PLAINS.get(h); + final List naturalSpawners = defaultBiome.spawners().get(EntityCategories.MONSTER.get()); + Collections.shuffle(naturalSpawners); + final List spawner = Arrays.asList(naturalSpawners.get(0)); - final BiomeTemplate template = BiomeTemplate.builder().fromValue(defaultBiome) - .add(Keys.FEATURES, Map.of(DecorationSteps.LAKES.get(), List.of(PlacedFeatures.LAKE_LAVA_SURFACE.get()))) - .add(Keys.CARVERS, Map.of()) + s.register(ResourceKey.of(NAMESPACE, CUSTOM_PLAINS), Biome.builder().from(defaultBiome) + .add(Keys.FEATURES, Map.of(DecorationSteps.LAKES.get(), List.of(PlacedFeatures.LAKE_LAVA_SURFACE.get(h)))) + .add(Keys.CARVERS, List.of()) .add(Keys.NATURAL_SPAWNERS, Map.of(EntityCategories.MONSTER.get(), spawner)) - .add(Keys.AMBIENT_ADDITIONAL_SOUND, SoundConfig.factory().ofAdditional(SoundTypes.ENTITY_CREEPER_PRIMED.get(), 0.001)) - .add(Keys.AMBIENT_PARTICLE, ParticleConfig.of(ParticleTypes.BUBBLE.get(), 0.01f)) - .key(ResourceKey.of(NAMESPACE, CUSTOM_PLAINS)).build(); - Sponge.server().dataPackManager().save(template); + .add(Keys.AMBIENT_ADDITIONAL_SOUND, SoundConfig.factory().ofAdditional(SoundTypes.ENTITY_CREEPER_PRIMED.get(h), 0.001)) + .add(Keys.AMBIENT_PARTICLE, ParticleConfig.of(ParticleTypes.BUBBLE.get(h), 0.01f)) + .build()); - final Biome defaultBiome2 = Biomes.FLOWER_FOREST.get(Sponge.server()); - final BiomeTemplate template2 = BiomeTemplate.builder().fromValue(defaultBiome2) + final Biome defaultBiome2 = Biomes.FLOWER_FOREST.get(h); + s.register(ResourceKey.of(NAMESPACE, CUSTOM_FOREST), Biome.builder().from(defaultBiome2) .add(Keys.NATURAL_SPAWNERS, Map.of(EntityCategories.MONSTER.get(), spawner)) - .key(ResourceKey.of(NAMESPACE, CUSTOM_FOREST)).build(); - Sponge.server().dataPackManager().save(template2); - - return CommandResult.success(); + .build()); + }, RegistryTypes.PLACED_FEATURE); } private CommandResult list(CommandContext ctx, final Parameter.Value filterParam) { @@ -135,16 +128,6 @@ private CommandResult list(CommandContext ctx, final Parameter.Value fil final Registry registry = this.registry(); registry.streamEntries().filter(e -> invert == e.key().toString().toUpperCase().contains(filter)) .forEach(e -> ctx.sendMessage(Identity.nil(), Component.text(" - " + e.key(), NamedTextColor.GRAY))); - - final DataPackManager dpm = Sponge.server().dataPackManager(); - dpm.find(DataPackTypes.BIOME).forEach((pack, keys) -> { - ctx.sendMessage(Identity.nil(), Component.text(pack.name() + ": " + pack.description() , NamedTextColor.DARK_AQUA)); - keys.forEach(key -> ctx.sendMessage(Identity.nil(), Component.text(" - " + key, NamedTextColor.GRAY))); - }); - - dpm.load(DataPacks.BIOME, ResourceKey.of(NAMESPACE, CUSTOM_PLAINS)).join().ifPresent(template -> { - ctx.sendMessage(Identity.nil(), Component.text("BiomeTemplate loaded from disk is present " + template.key(), NamedTextColor.DARK_AQUA)); - }); return CommandResult.success(); } @@ -159,7 +142,6 @@ public Command.Parameterized biomeCmd() { return Command.builder() .addChild(Command.builder().addParameter(filter).executor(ctx -> this.list(ctx, filter)).build(), "list") .addChild(Command.builder().addParameter(biomeParam).executor(ctx -> this.info(ctx, biomeParam)).build(), "info") - .addChild(Command.builder().executor(this::register).build(), "register") .build(); } } diff --git a/testplugins/src/main/java/org/spongepowered/test/worldgen/CarverTest.java b/testplugins/src/main/java/org/spongepowered/test/worldgen/CarverTest.java index bcea24e7cfe..3880a25e97d 100644 --- a/testplugins/src/main/java/org/spongepowered/test/worldgen/CarverTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/worldgen/CarverTest.java @@ -28,17 +28,15 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.Sponge; import org.spongepowered.api.command.Command; import org.spongepowered.api.command.CommandResult; import org.spongepowered.api.command.parameter.CommandContext; -import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; +import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.world.generation.carver.Carver; -import org.spongepowered.api.world.generation.carver.CarverTemplate; import org.spongepowered.api.world.generation.carver.CarverTypes; import org.spongepowered.api.world.generation.carver.Carvers; -import java.io.IOException; public class CarverTest { @@ -52,20 +50,14 @@ private CommandResult carvers(final CommandContext ctx) { ctx.sendMessage(Identity.nil(), Component.text(" - " + e.key(), NamedTextColor.GRAY)); }); - final CarverTemplate template = CarverTemplate.builder().fromValue(Carvers.CAVE.get()).key(ResourceKey.of("carvertest", "custom_carver")).build(); - final DataContainer container = template.toContainer(); - try { - final CarverTemplate rebuiltTemplate = CarverTemplate.builder().fromDataPack(container).key(ResourceKey.of("carvertest", "custom_carver")).build(); - Sponge.server().dataPackManager().save(rebuiltTemplate); - - } catch (IOException e) { - throw new IllegalStateException(e); - } - final Carver reconfigured = CarverTypes.CAVE.get().configure(container); - return CommandResult.success(); } + public void register(final RegisterRegistryValueEvent event) { + event.registry(RegistryTypes.CARVER, (h, s) -> + s.register(ResourceKey.of("carvertest", "custom_carver"), Carver.builder().from(Carvers.CAVE.get(h)).build())); + } + Command.Parameterized carverCmd() { return Command.builder() diff --git a/testplugins/src/main/java/org/spongepowered/test/worldgen/FeatureTest.java b/testplugins/src/main/java/org/spongepowered/test/worldgen/FeatureTest.java index a2179accebf..d53a0a6e6e3 100644 --- a/testplugins/src/main/java/org/spongepowered/test/worldgen/FeatureTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/worldgen/FeatureTest.java @@ -35,17 +35,17 @@ import org.spongepowered.api.command.parameter.CommandContext; import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; +import org.spongepowered.api.registry.DefaultedRegistryReference; +import org.spongepowered.api.registry.RegistryRegistrationSet; import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.util.Direction; import org.spongepowered.api.util.blockray.RayTrace; import org.spongepowered.api.world.LocatableBlock; import org.spongepowered.api.world.generation.feature.Feature; -import org.spongepowered.api.world.generation.feature.FeatureTemplate; import org.spongepowered.api.world.generation.feature.Features; import org.spongepowered.api.world.generation.feature.PlacedFeature; -import org.spongepowered.api.world.generation.feature.PlacedFeatureTemplate; import org.spongepowered.api.world.generation.feature.PlacedFeatures; -import org.spongepowered.api.world.server.DataPackManager; import org.spongepowered.api.world.server.ServerLocation; import java.util.Optional; @@ -53,6 +53,38 @@ public class FeatureTest { + public static final class CustomFeatures { + + public static final class Holder { + + public static final RegistryRegistrationSet FEATURES = CustomFeatures.BUILDER.build(); + } + + private static final RegistryRegistrationSet.Builder BUILDER = RegistryRegistrationSet.builder(RegistryTypes.FEATURE, Sponge::server); + + public static final DefaultedRegistryReference FEATURE = CustomFeatures.BUILDER.register( + ResourceKey.of("featuretest", "test"), + (h) -> Feature.builder().from(Features.TREES_PLAINS.get(h)).build()); + } + + public static final class CustomPlacedFeatures { + + public static final class Holder { + + public static final RegistryRegistrationSet PLACED_FEATURES = CustomPlacedFeatures.BUILDER.build(); + } + + private static final RegistryRegistrationSet.Builder BUILDER = RegistryRegistrationSet.builder(RegistryTypes.PLACED_FEATURE, Sponge::server); + + public static final DefaultedRegistryReference PLACED_FEATURE = CustomPlacedFeatures.BUILDER.register( + ResourceKey.of("featuretest", "test"), + (h) -> PlacedFeature.builder().from(PlacedFeatures.TREES_PLAINS.get(h)).build()); + + public static final DefaultedRegistryReference PLACED_FEATURE_2 = CustomPlacedFeatures.BUILDER.register( + ResourceKey.of("featuretest", "test2"), + (h) -> PlacedFeature.builder().from(PlacedFeatures.TREES_PLAINS.get(h)).feature(CustomFeatures.FEATURE.get(h)).build()); + } + CommandResult placeFeature(final CommandContext commandContext, final Parameter.Value param) { return FeatureTest.place(commandContext, param, Features.TREES_PLAINS.get(), Feature::place); } @@ -84,30 +116,6 @@ public static RayTrace viewRay(final ServerPlayer player) { return RayTrace.block().select(RayTrace.nonAir()).limit(100).sourceEyePosition(player).direction(player); } - CommandResult registerFeature(final CommandContext ctx) { - final DataPackManager dpm = Sponge.server().dataPackManager(); - - final FeatureTemplate featureTemplate = FeatureTemplate.builder().fromValue(Features.TREES_PLAINS.get()) - .key(ResourceKey.of("featuretest", "test")) - .build(); - - final PlacedFeatureTemplate placedFeatureTemplate1 = PlacedFeatureTemplate.builder().fromValue(PlacedFeatures.TREES_PLAINS.get()) - .key(ResourceKey.of("featuretest", "test")) - .build(); - - final PlacedFeatureTemplate placedFeatureTemplate2 = PlacedFeatureTemplate.builder().fromValue(PlacedFeatures.TREES_PLAINS.get()) - .feature(featureTemplate) - .key(ResourceKey.of("featuretest", "test2")) - .build(); - - - dpm.save(featureTemplate); - dpm.save(placedFeatureTemplate1); - dpm.save(placedFeatureTemplate2); - - return CommandResult.success(); - } - private CommandResult listFeatures(CommandContext ctx, final Parameter.Value filterParam) { final Optional rawFilter = ctx.one(filterParam); final String filter = rawFilter.orElse("minecraft:").toUpperCase(); @@ -131,11 +139,11 @@ Command.Parameterized featureCmd() { .addChild(Command.builder().addParameter(filter).executor(ctx -> this.listFeatures(ctx, filter)).build(), "list") .addChild(Command.builder().addParameter(feature).executor(ctx -> this.placeFeature(ctx, feature)).build(), "placeFeature") .addChild(Command.builder().addParameter(placedFeature).executor(ctx -> this.placePlaced(ctx, placedFeature)).build(), "placePlaced") - .addChild(Command.builder().executor(this::registerFeature).build(), "register") .build(); } - - - + public void register(final RegisterRegistryValueEvent event) { + event.register(FeatureTest.CustomFeatures.Holder.FEATURES); + event.register(FeatureTest.CustomPlacedFeatures.Holder.PLACED_FEATURES, FeatureTest.CustomFeatures.Holder.FEATURES.registryType()); + } } diff --git a/testplugins/src/main/java/org/spongepowered/test/worldgen/NoiseTest.java b/testplugins/src/main/java/org/spongepowered/test/worldgen/NoiseTest.java index ac91d25c63c..14781e4a5b6 100644 --- a/testplugins/src/main/java/org/spongepowered/test/worldgen/NoiseTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/worldgen/NoiseTest.java @@ -28,18 +28,17 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.Sponge; import org.spongepowered.api.command.Command; import org.spongepowered.api.command.CommandResult; import org.spongepowered.api.command.parameter.CommandContext; import org.spongepowered.api.command.parameter.Parameter; -import org.spongepowered.api.world.generation.config.noise.DensityFunctionTemplate; +import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; +import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.world.generation.config.noise.DensityFunctions; -import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfigTemplate; +import org.spongepowered.api.world.generation.config.noise.Noise; +import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfig; import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfigs; -import org.spongepowered.api.world.generation.config.noise.NoiseTemplate; import org.spongepowered.api.world.generation.config.noise.Noises; -import org.spongepowered.api.world.server.DataPackManager; import java.util.Optional; @@ -65,37 +64,21 @@ private CommandResult listNoise(final CommandContext ctx, final Parameter.Value< return CommandResult.success(); } - private CommandResult registerNoise(final CommandContext commandContext) { - final DataPackManager dpm = Sponge.server().dataPackManager(); + public void register(final RegisterRegistryValueEvent event) { + event.registry(RegistryTypes.NOISE_GENERATOR_CONFIG, (h, s) -> + s.register(ResourceKey.of("noisetest", "test"), NoiseGeneratorConfig.builder().from(NoiseGeneratorConfigs.CAVES.get(h)).build())); - final NoiseGeneratorConfigTemplate noiseGenCfgTemplate = NoiseGeneratorConfigTemplate.builder().fromValue(NoiseGeneratorConfigs.CAVES.get()) - .key(ResourceKey.of("noisetest", "test")) - .build(); - - final NoiseTemplate noiseTemplate = NoiseTemplate.builder().fromValue(Noises.CALCITE.get()) - .key(ResourceKey.of("noisetest", "calcite_copy")) - .build(); + event.registry(RegistryTypes.NOISE, (h, s) -> + s.register(ResourceKey.of("noisetest", "calcite_copy"), Noise.builder().from(Noises.CALCITE.get(h)).build())); - final DensityFunctionTemplate overWorldDensity = DensityFunctionTemplate.builder().fromValue(DensityFunctions.OVERWORLD_BASE_3D_NOISE.get()) - .key(ResourceKey.of("noisetest", "overworld_base3d_copy")) - .build(); - - try { - dpm.save(noiseGenCfgTemplate); - dpm.save(noiseTemplate); - dpm.save(overWorldDensity); - } catch (Exception e) { - e.printStackTrace(); - } - - return CommandResult.success(); +// event.registry(RegistryTypes.DENSITY_FUNCTION, (h, s) -> +// s.register(ResourceKey.of("noisetest", "overworld_base3d_copy"), DensityFunction.builder().build())); } Command.Parameterized noiseCmd() { final Parameter.Value filter = Parameter.string().key("filter").optional().build(); return Command.builder() .addChild(Command.builder().addParameter(filter).executor(ctx -> this.listNoise(ctx, filter)).build(), "list") - .addChild(Command.builder().executor(this::registerNoise).build(), "register") .build(); } diff --git a/testplugins/src/main/java/org/spongepowered/test/worldgen/StructureTest.java b/testplugins/src/main/java/org/spongepowered/test/worldgen/StructureTest.java index 249bbec70a8..840756c8bfe 100644 --- a/testplugins/src/main/java/org/spongepowered/test/worldgen/StructureTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/worldgen/StructureTest.java @@ -31,47 +31,35 @@ import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; import org.spongepowered.api.command.Command; -import org.spongepowered.api.command.CommandCompletion; import org.spongepowered.api.command.CommandResult; import org.spongepowered.api.command.parameter.CommandContext; import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPackTypes; -import org.spongepowered.api.datapack.DataPacks; import org.spongepowered.api.entity.living.player.server.ServerPlayer; -import org.spongepowered.api.event.cause.entity.SpawnTypes; +import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; import org.spongepowered.api.registry.Registry; import org.spongepowered.api.registry.RegistryTypes; -import org.spongepowered.api.tag.BiomeTags; import org.spongepowered.api.util.Direction; import org.spongepowered.api.util.blockray.RayTrace; import org.spongepowered.api.world.LocatableBlock; import org.spongepowered.api.world.biome.Biome; import org.spongepowered.api.world.generation.feature.DecorationSteps; -import org.spongepowered.api.world.generation.structure.SchematicTemplate; import org.spongepowered.api.world.generation.structure.Structure; import org.spongepowered.api.world.generation.structure.StructurePlacement; -import org.spongepowered.api.world.generation.structure.StructureSetTemplate; +import org.spongepowered.api.world.generation.structure.StructureSet; import org.spongepowered.api.world.generation.structure.StructureSets; -import org.spongepowered.api.world.generation.structure.StructureTemplate; import org.spongepowered.api.world.generation.structure.StructureTypes; import org.spongepowered.api.world.generation.structure.Structures; import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPool; import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPoolElement; -import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPoolTemplate; import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPools; import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorList; -import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorListTemplate; import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorLists; -import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorTypes; -import org.spongepowered.api.world.schematic.Schematic; -import org.spongepowered.api.world.server.DataPackManager; +import org.spongepowered.api.world.generation.structure.jigsaw.Processors; import org.spongepowered.api.world.server.ServerLocation; -import org.spongepowered.math.vector.Vector3i; import java.io.IOException; -import java.util.Collection; +import java.util.List; import java.util.Optional; import java.util.Random; @@ -81,41 +69,44 @@ private CommandResult placeStructure(final CommandContext commandContext, final return FeatureTest.place(commandContext, param, Structures.DESERT_PYRAMID.get(), Structure::place); } - private CommandResult registerStructure(final CommandContext ctx) { - final DataPackManager dpm = Sponge.server().dataPackManager(); + public void register(final RegisterRegistryValueEvent event) { +// event.registry(RegistryTypes.STRUCTURE, (h, s) -> +// s.register(ResourceKey.of("featuretest", "test"), Structure.builder().build())); - final StructureTemplate structureTemplate = StructureTemplate.builder().fromValue(Structures.IGLOO.get()) - .key(ResourceKey.of("featuretest", "test")) - .build(); - - dpm.save(structureTemplate); - - return CommandResult.success(); - } - - private CommandResult registerStructureSets(final CommandContext ctx) { - final DataPackManager dpm = Sponge.server().dataPackManager(); + event.registry(RegistryTypes.STRUCTURE_SET, (h, s) -> { + s.register(ResourceKey.of("structuresettest", "igloo_2"), StructureSet.builder().from(StructureSets.IGLOOS.get(h)).build()); - final StructureSetTemplate template1 = StructureSetTemplate.builder().fromValue(StructureSets.IGLOOS.get()) - .key(ResourceKey.of("structuresettest", "igloo_2")) - .build(); + s.register(ResourceKey.of("structuresettest", "igloo_or_hut"), StructureSet.builder() + .add(Structures.IGLOO.get(h), 1) + .add(Structures.SWAMP_HUT.get(h), 1) + .placement(StructurePlacement.builder().randomSpread(1).spacing(16).separation(8).build()) + .build()); + }, RegistryTypes.STRUCTURE); - final StructurePlacement placement = StructurePlacement.builder().randomSpread(1).spacing(16).separation(8).build(); - final StructureSetTemplate template2 = StructureSetTemplate.builder() - .add(Structures.IGLOO.get(), 1) - .add(Structures.SWAMP_HUT.get(), 1) - .placement(placement) - .key(ResourceKey.of("structuresettest", "igloo_or_hut")) - .build(); + event.registry(RegistryTypes.PROCESSOR_LIST, (h, s) -> + s.register(ResourceKey.of("processortest", "test"), ProcessorList.builder().from(ProcessorLists.MOSSIFY_70_PERCENT.get(h)).build())); - dpm.save(template1); - dpm.save(template2); + event.registry(RegistryTypes.JIGSAW_POOL, (h, s) -> { + s.register(ResourceKey.of("jigsawtest", "test"), JigsawPool.builder().from(JigsawPools.VILLAGE_DESERT_DECOR.get(h)).build()); - final StructurePlacement strongholdPlacement = StructurePlacement.builder().concentricRings(Vector3i.ONE, 1) - .distance(32).spread(3).count(128).preferredBiomes(BiomeTags.STRONGHOLD_BIASED_TO) - .build(); + final JigsawPoolElement.Projection projection = JigsawPoolElement.factory().rigid(); + final ProcessorList noProcessing = ProcessorLists.EMPTY.get(h); + final JigsawPoolElement element1 = JigsawPoolElement.factory().single(ResourceKey.minecraft("village/desert/desert_lamp_1"), noProcessing).apply(projection); + final JigsawPoolElement element2 = JigsawPoolElement.factory().single(ResourceKey.minecraft("village/plains/plains_lamp_1"), noProcessing).apply(projection); + final JigsawPoolElement element3 = JigsawPoolElement.factory().single(ResourceKey.minecraft("village/snowy/snowy_lamp_post_01"), noProcessing).apply(projection); + final JigsawPoolElement element4 = JigsawPoolElement.factory().single(ResourceKey.minecraft("village/savanna/savanna_lamp_post_01"), noProcessing).apply(projection); + final JigsawPoolElement element5 = JigsawPoolElement.factory().single(ResourceKey.minecraft("village/taiga/taiga_lamp_post_1"), noProcessing).apply(projection); - return CommandResult.success(); + final ResourceKey lamptest = ResourceKey.of("jigsawtest", "lamps"); + s.register(lamptest, JigsawPool.builder() + .add(element1, 1) + .add(element2, 1) + .add(element3, 1) + .add(element4, 1) + .add(element5, 1) + .name(lamptest) + .build()); + }); } private CommandResult listStructures(CommandContext ctx, final Parameter.Value filterParam) { @@ -167,7 +158,7 @@ private CommandResult listSchematics(final CommandContext ctx, final Parameter.V final String filter = rawFilter.orElse("minecraft:").toUpperCase(); boolean invert = rawFilter.isPresent(); - final DataPackManager dpm = Sponge.server().dataPackManager(); + /*final DataPackManager dpm = Sponge.server().dataPackManager(); dpm.find(DataPackTypes.SCHEMATIC).forEach((pack, keys) -> { if (keys.isEmpty()) { return; @@ -179,7 +170,7 @@ private CommandResult listSchematics(final CommandContext ctx, final Parameter.V ctx.sendMessage(Identity.nil(), Component.text(" - " + key + " loaded " + loaded.isPresent(), NamedTextColor.GRAY)); }); - }); + });*/ return CommandResult.success(); } @@ -199,20 +190,20 @@ private CommandResult listJigsaw(final CommandContext ctx, final Parameter.Value private CommandResult placeSchematic(final CommandContext ctx, final Parameter.Value schematicParam) { - final DataPackManager dpm = Sponge.server().dataPackManager(); - final ResourceKey key = ctx.one(schematicParam).orElse(ResourceKey.minecraft("village/desert/houses/desert_small_house_1")); - final Optional> pack = dpm.findPack(DataPackTypes.SCHEMATIC, key); - final Optional player = ctx.cause().first(ServerPlayer.class); - if (pack.isPresent()) { - final Optional loaded = dpm.load(pack.get(), key).join(); - if (loaded.isPresent() && player.isPresent()) { - final Schematic schematic = loaded.get().schematic(); - final RayTrace ray = FeatureTest.viewRay(player.get()); - final ServerLocation location = ray.execute().orElseThrow().selectedObject().serverLocation().relativeTo(Direction.UP); - schematic.applyToWorld(location.world(), location.blockPosition(), SpawnTypes.CUSTOM); - ctx.sendMessage(Identity.nil(), Component.text("Placed Schematic: " + key + " from pack " + pack.get().name(), NamedTextColor.DARK_AQUA)); - } - } +// final DataPackManager dpm = Sponge.server().dataPackManager(); +// final ResourceKey key = ctx.one(schematicParam).orElse(ResourceKey.minecraft("village/desert/houses/desert_small_house_1")); +// final Optional> pack = dpm.findPack(DataPackTypes.SCHEMATIC, key); +// final Optional player = ctx.cause().first(ServerPlayer.class); +// if (pack.isPresent()) { +// final Optional loaded = dpm.load(pack.get(), key).join(); +// if (loaded.isPresent() && player.isPresent()) { +// final Schematic schematic = loaded.get().schematic(); +// final RayTrace ray = FeatureTest.viewRay(player.get()); +// final ServerLocation location = ray.execute().orElseThrow().selectedObject().serverLocation().relativeTo(Direction.UP); +// schematic.applyToWorld(location.world(), location.blockPosition(), SpawnTypes.CUSTOM); +// ctx.sendMessage(Identity.nil(), Component.text("Placed Schematic: " + key + " from pack " + pack.get().name(), NamedTextColor.DARK_AQUA)); +// } +// } return CommandResult.success(); } @@ -257,68 +248,22 @@ private CommandResult listProcessors(final CommandContext ctx, final Parameter.V ctx.sendMessage(Identity.nil(), Component.text("Processor Types:", NamedTextColor.DARK_AQUA)); - ProcessorTypes.registry().streamEntries().filter(e -> invert == e.key().toString().toUpperCase().contains(filter)) - .forEach(e -> ctx.sendMessage(Identity.nil(), Component.text(" - " + e.key(), NamedTextColor.GRAY))); - - return CommandResult.success(); - } - - private CommandResult registerProcessor(final CommandContext ctx) { - final DataPackManager dpm = Sponge.server().dataPackManager(); - - - final ProcessorListTemplate template = ProcessorListTemplate.builder() - .fromValue(ProcessorLists.MOSSIFY_70_PERCENT.get()) - .key(ResourceKey.of("processortest", "test")) - .build(); - - dpm.save(template); - - return CommandResult.success(); - } - - private CommandResult registerJigsaw(final CommandContext ctx) { - final DataPackManager dpm = Sponge.server().dataPackManager(); - - - final JigsawPoolTemplate template1 = JigsawPoolTemplate.builder() - .fromValue(JigsawPools.VILLAGE_DESERT_DECOR.get()) - .key(ResourceKey.of("jigsawtest", "test")) - .build(); - - - final JigsawPoolElement.Projection projection = JigsawPoolElement.factory().rigid(); - final ProcessorList noProcessing = ProcessorLists.EMPTY.get(); - final JigsawPoolElement element1 = JigsawPoolElement.factory().single(ResourceKey.minecraft("village/desert/desert_lamp_1"), noProcessing).apply(projection); - final JigsawPoolElement element2 = JigsawPoolElement.factory().single(ResourceKey.minecraft("village/plains/plains_lamp_1"), noProcessing).apply(projection); - final JigsawPoolElement element3 = JigsawPoolElement.factory().single(ResourceKey.minecraft("village/snowy/snowy_lamp_post_01"), noProcessing).apply(projection); - final JigsawPoolElement element4 = JigsawPoolElement.factory().single(ResourceKey.minecraft("village/savanna/savanna_lamp_post_01"), noProcessing).apply(projection); - final JigsawPoolElement element5 = JigsawPoolElement.factory().single(ResourceKey.minecraft("village/taiga/taiga_lamp_post_1"), noProcessing).apply(projection); - final ResourceKey lamptest = ResourceKey.of("jigsawtest", "lamps"); - final JigsawPoolTemplate template2 = JigsawPoolTemplate.builder() - .add(element1, 1) - .add(element2, 1) - .add(element3, 1) - .add(element4, 1) - .add(element5, 1) - .name(lamptest) - .key(lamptest) - .build(); - - dpm.save(template1); - dpm.save(template2); + java.util.stream.Stream.of( + Processors.BLACKSTONE_REPLACE, Processors.BLOCK_AGE, Processors.BLOCK_IGNORE, Processors.BLOCK_ROT, + Processors.CAPPED, Processors.GRAVITY, Processors.JIGSAW_REPLACEMENT, Processors.LAVA_SUBMERGED_BLOCK, + Processors.NOP, Processors.PROTECTED_BLOCKS, Processors.RULE) + .filter(k -> invert == k.toString().toUpperCase().contains(filter)) + .forEach(k -> ctx.sendMessage(Identity.nil(), Component.text(" - " + k, NamedTextColor.GRAY))); return CommandResult.success(); } - Command.Parameterized structureCmd() { final Parameter.Value structure = Parameter.registryElement(TypeToken.get(Structure.class), RegistryTypes.STRUCTURE, "minecraft").key("structure").optional().build(); final Parameter.Value filter = Parameter.string().key("filter").optional().build(); return Command.builder() .addChild(Command.builder().addParameter(structure).executor(ctx -> this.placeStructure(ctx, structure)).build(), "placeStructure") .addChild(Command.builder().addParameter(filter).executor(ctx -> this.listStructures(ctx, filter)).build(), "list") - .addChild(Command.builder().executor(this::registerStructure).build(), "register") .addChild(Command.builder().addParameter(structure).executor(ctx -> this.structureInfo(ctx, structure)).build(), "info") .build(); } @@ -327,7 +272,6 @@ Command.Parameterized setsCmd() { final Parameter.Value filter = Parameter.string().key("filter").optional().build(); return Command.builder() .addChild(Command.builder().addParameter(filter).executor(ctx -> this.listStructureSets(ctx, filter)).build(), "list") - .addChild(Command.builder().executor(this::registerStructureSets).build(), "register") .build(); } @@ -335,10 +279,11 @@ Command.Parameterized schematicCmd() { final Parameter.Value processorList = Parameter.registryElement(TypeToken.get(ProcessorList.class), RegistryTypes.PROCESSOR_LIST, "minecraft").key("processor_list").optional().build(); final Parameter.Value schematic = Parameter.resourceKey().key("schematic").optional().build(); final Parameter.Value structure = Parameter.resourceKey().key("structure").completer((ctx, input) -> { - final DataPackManager dpm = Sponge.server().dataPackManager(); - return dpm.find(DataPackTypes.SCHEMATIC).values().stream().flatMap(Collection::stream).map(ResourceKey::toString) - .filter(s -> s.startsWith(input) || s.startsWith("minecraft:" + input)) - .map(CommandCompletion::of).toList(); + return List.of(); +// final DataPackManager dpm = Sponge.server().dataPackManager(); +// return dpm.find(DataPackTypes.SCHEMATIC).values().stream().flatMap(Collection::stream).map(ResourceKey::toString) +// .filter(s -> s.startsWith(input) || s.startsWith("minecraft:" + input)) +// .map(CommandCompletion::of).toList(); }).optional().build(); final Parameter.Value filter = Parameter.string().key("filter").optional().build(); return Command.builder() @@ -354,7 +299,6 @@ Command.Parameterized jigsawCmd() { return Command.builder() .addChild(Command.builder().addParameter(jigsawPool).executor(ctx -> this.placeJigsaw(ctx, jigsawPool)).build(), "place") .addChild(Command.builder().addParameter(filter).executor(ctx -> this.listJigsaw(ctx, filter)).build(), "list") - .addChild(Command.builder().executor(this::registerJigsaw).build(), "register") .build(); } @@ -362,7 +306,6 @@ Command.Parameterized processorCmd() { final Parameter.Value filter = Parameter.string().key("filter").optional().build(); return Command.builder() .addChild(Command.builder().addParameter(filter).executor(ctx -> this.listProcessors(ctx, filter)).build(), "list") - .addChild(Command.builder().executor(this::registerProcessor).build(), "register") .build(); } diff --git a/testplugins/src/main/java/org/spongepowered/test/worldgen/WorldGenTest.java b/testplugins/src/main/java/org/spongepowered/test/worldgen/WorldGenTest.java index abab4bb1adc..fc3ae38cc06 100644 --- a/testplugins/src/main/java/org/spongepowered/test/worldgen/WorldGenTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/worldgen/WorldGenTest.java @@ -39,6 +39,7 @@ import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; +import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; import org.spongepowered.api.registry.Registry; import org.spongepowered.api.registry.RegistryEntry; import org.spongepowered.api.registry.RegistryReference; @@ -50,7 +51,6 @@ import org.spongepowered.api.world.SerializationBehavior; import org.spongepowered.api.world.WorldType; import org.spongepowered.api.world.WorldTypeEffects; -import org.spongepowered.api.world.WorldTypeTemplate; import org.spongepowered.api.world.WorldTypes; import org.spongepowered.api.world.biome.AttributedBiome; import org.spongepowered.api.world.biome.Biome; @@ -60,13 +60,16 @@ import org.spongepowered.api.world.biome.provider.MultiNoiseBiomeConfig; import org.spongepowered.api.world.generation.ChunkGenerator; import org.spongepowered.api.world.generation.config.SurfaceRule; +import org.spongepowered.api.world.generation.config.WorldGenerationConfig; import org.spongepowered.api.world.generation.config.noise.NoiseConfig; import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfig; import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfigs; import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; +import org.spongepowered.api.world.server.WorldArchetype; +import org.spongepowered.api.world.server.WorldArchetypeType; import org.spongepowered.api.world.server.WorldManager; -import org.spongepowered.api.world.server.WorldTemplate; +import org.spongepowered.api.world.server.storage.ServerWorldProperties; import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.builtin.jvm.Plugin; import org.spongepowered.test.world.WorldTest; @@ -109,27 +112,20 @@ private void onRegisterCommand(final RegisterCommandEvent .addChild(Command.builder().executor(this::createRandomWorld).build(), "createrandomworld", "crw") .addChild(Command.builder().executor(this::world).build(), "world") .addChild(Command.builder().addParameter(stringParam).executor(commandContext -> this.seededWorld(commandContext, stringParam)).build(), "seededworld") - .addChild(Command.builder().executor(this::worldType).build(), "worldtype") .build() ,"wgentest") ; } - private CommandResult seededWorld(final CommandContext ctx, final Parameter.Value stringParam) { - final String seed = ctx.one(stringParam).orElse("random"); - final WorldTemplate template = - WorldTemplate.builder().from(WorldTemplate.overworld()).add(Keys.SEED, (long) seed.hashCode()) - .key(ResourceKey.of(this.plugin, seed)) - .add(Keys.IS_LOAD_ON_STARTUP, false) - .add(Keys.SERIALIZATION_BEHAVIOR, SerializationBehavior.NONE) - .build(); - final Optional optPlayer = ctx.cause().first(ServerPlayer.class); - Sponge.server().worldManager().loadWorld(template).thenAccept(w -> optPlayer.ifPresent(player -> WorldTest.transportToWorld(player, w))); - return CommandResult.success(); - } - - private CommandResult worldType(final CommandContext commandContext) { - final WorldTypeTemplate.Builder builder = WorldTypeTemplate.builder() + @Listener + private void onRegisterRegistryValue(final RegisterRegistryValueEvent event) { + this.biomeTest.register(event); + this.carverTest.register(event); + this.featureTest.register(event); + this.noiseTest.register(event); + this.structureTest.register(event); + event.registry(RegistryTypes.WORLD_TYPE, r -> + r.register(ResourceKey.of(this.plugin, "customworldtype"), WorldType.builder() .add(Keys.WORLD_TYPE_EFFECT, WorldTypeEffects.OVERWORLD) .add(Keys.SCORCHING, true) .add(Keys.NATURAL_WORLD_TYPE, false) @@ -147,10 +143,19 @@ private CommandResult worldType(final CommandContext commandContext) { .add(Keys.SPAWN_LIGHT_LIMIT, 5) .add(Keys.SPAWN_LIGHT_RANGE, Range.intRange(5, 10)) .add(Keys.AMBIENT_LIGHTING, 0.5f) - .add(Keys.CREATE_DRAGON_FIGHT, true) - .key(ResourceKey.of(this.plugin, "customworldtype")); - final WorldTypeTemplate template = builder.build(); - Sponge.server().dataPackManager().save(template); + .add(Keys.CREATE_DRAGON_FIGHT, true).build())); + } + + private CommandResult seededWorld(final CommandContext ctx, final Parameter.Value stringParam) { + final String seed = ctx.one(stringParam).orElse("random"); + final Optional optPlayer = ctx.cause().first(ServerPlayer.class); + Sponge.server().worldManager().loadWorld(ResourceKey.of(this.plugin, seed), + ServerWorldProperties.LoadOptions.create( + WorldArchetype.of( + WorldArchetypeType.of(WorldTypes.OVERWORLD.get(), ChunkGenerator.overworld()), + WorldGenerationConfig.builder().seed(seed.hashCode()).build()), + (properties) -> properties.offer(Keys.SERIALIZATION_BEHAVIOR, SerializationBehavior.NONE))) + .thenAccept(w -> w.ifPresent(world -> optPlayer.ifPresent(player -> WorldTest.transportToWorld(player, world)))); return CommandResult.success(); } @@ -167,9 +172,9 @@ private CommandResult world(CommandContext commandContext) { .addBiome(AttributedBiome.of(ref2, BiomeAttributes.point(0.4f, -1f, 0.5f, -0.2f, 0f, -0.95f, 0.0f))) .build(); final var chunkGen = ChunkGenerator.noise(BiomeProvider.multiNoise(cfg), ChunkGenerator.overworld().config()); - final WorldTemplate template = WorldTemplate.builder().add(Keys.CHUNK_GENERATOR, chunkGen).key(worldKey).build(); final Optional optPlayer = commandContext.cause().first(ServerPlayer.class); - wm.loadWorld(template).thenAccept(w -> optPlayer.ifPresent(player -> WorldTest.transportToWorld(player, w))); + wm.loadWorld(worldKey, ServerWorldProperties.LoadOptions.create(WorldArchetype.of(WorldArchetypeType.of(WorldTypes.OVERWORLD.get(), chunkGen)))) + .thenAccept(w -> w.ifPresent(world -> optPlayer.ifPresent(player -> WorldTest.transportToWorld(player, world)))); } return CommandResult.success(); } @@ -234,12 +239,12 @@ private CommandResult createRandomWorld(final CommandContext context) { final NoiseGeneratorConfig noiseGenConfig = NoiseGeneratorConfig.builder() .spawnTargets(NoiseGeneratorConfigs.OVERWORLD.get().spawnTargets()) + .noiseRouter(NoiseGeneratorConfigs.OVERWORLD.get().noiseRouter()) .noiseConfig(noiseConfig) .surfaceRule(customSurfaceRule) // .seaLevel(random.nextInt(61 - 1) + 1 + random.nextInt(30)) // 2 rolls .seaLevel(63) - .key(ResourceKey.of(this.plugin, "test")) - .build().config(); + .build(); final ResourceKey worldKey = ResourceKey.of(this.plugin, owner.toLowerCase()); final List attributedBiomes = biomes.stream().map(biomeRef -> { @@ -262,17 +267,6 @@ private CommandResult createRandomWorld(final CommandContext context) { final MultiNoiseBiomeConfig biomeCfg = MultiNoiseBiomeConfig.builder().addBiomes(attributedBiomes).build(); final Optional customworldtype = WorldTypes.registry().findValue(ResourceKey.of(this.plugin, "customworldtype")); - final WorldTemplate customTemplate = WorldTemplate.builder() - .from(WorldTemplate.overworld()) - .key(worldKey) - .add(Keys.WORLD_TYPE, customworldtype.orElse(WorldTypes.OVERWORLD.get())) - .add(Keys.SERIALIZATION_BEHAVIOR, SerializationBehavior.NONE) - .add(Keys.IS_LOAD_ON_STARTUP, false) - .add(Keys.PERFORM_SPAWN_LOGIC, true) - .add(Keys.SEED, random.nextLong()) - .add(Keys.DISPLAY_NAME, Component.text("Custom world by " + owner)) - .add(Keys.CHUNK_GENERATOR, ChunkGenerator.noise(BiomeProvider.multiNoise(biomeCfg), noiseGenConfig)) - .build(); if (player.world().key().equals(worldKey)) { final ServerWorld world = wm.world(DefaultWorldKeys.DEFAULT).get(); @@ -281,12 +275,19 @@ private CommandResult createRandomWorld(final CommandContext context) { context.sendMessage(Identity.nil(), Component.text("Generating your world...").append(Component.newline()) .append(Component.text("with " + biomes.size() + " biomes")).append(Component.newline()) ); - wm.deleteWorld(worldKey).thenCompose(b -> wm.loadWorld(customTemplate)).thenAccept(w -> WorldTest.transportToWorld(player, w)).exceptionally(e -> { - context.sendMessage(Identity.nil(), Component.text("Failed to teleport!", NamedTextColor.DARK_RED)); - e.printStackTrace(); - return null; - } - ); + wm.deleteWorld(worldKey).thenCompose(b -> wm.loadWorld(worldKey, ServerWorldProperties.LoadOptions.create( + WorldArchetype.of( + WorldArchetypeType.of(customworldtype.orElse(WorldTypes.OVERWORLD.get()), ChunkGenerator.noise(BiomeProvider.multiNoise(biomeCfg), noiseGenConfig)), + WorldGenerationConfig.builder().seed(random.nextLong()).build()), + (properties) -> { + properties.offer(Keys.SERIALIZATION_BEHAVIOR, SerializationBehavior.NONE); + properties.offer(Keys.DISPLAY_NAME, Component.text("Custom world by " + owner)); + }) + )).thenAccept(w -> w.ifPresent(world -> WorldTest.transportToWorld(player, world))).exceptionally(e -> { + context.sendMessage(Identity.nil(), Component.text("Failed to teleport!", NamedTextColor.DARK_RED)); + e.printStackTrace(); + return null; + }); return CommandResult.success(); diff --git a/testplugins/src/main/resource-templates/META-INF/sponge_plugins.json b/testplugins/src/main/resource-templates/META-INF/sponge_plugins.json index 8af3eb43009..ea4cbd7e716 100644 --- a/testplugins/src/main/resource-templates/META-INF/sponge_plugins.json +++ b/testplugins/src/main/resource-templates/META-INF/sponge_plugins.json @@ -372,11 +372,29 @@ } ] }, + { + "id": "attributedata", + "name": "Attribute Data", + "entrypoint": "org.spongepowered.test.data.AttributeDataTest", + "description": "Attribute Data" + }, { "id": "decoratedpotdata", "name": "Decorated Pot Data", "entrypoint": "org.spongepowered.test.data.PotDataTest", "description": "Decorated Pot Data" + }, + { + "id": "viewertest", + "name": "Viewer Test", + "entrypoint": "org.spongepowered.test.viewer.ViewerTest", + "description": "Viewer tests" + }, + { + "id": "consumabledata", + "name": "Consumable ItemStack Data", + "entrypoint": "org.spongepowered.test.data.ConsumableDataTest", + "description": "Consumable ItemStack Data" } ] } diff --git a/vanilla/build.gradle.kts b/vanilla/build.gradle.kts index bebd72ae524..f9274e9aa80 100644 --- a/vanilla/build.gradle.kts +++ b/vanilla/build.gradle.kts @@ -11,7 +11,6 @@ plugins { val commonProject = parent!! val bootstrapProject = commonProject.project(":bootstrap") -val transformersProject = commonProject.project(":modlauncher-transformers") val libraryManagerProject = commonProject.project(":library-manager") val testPluginsProject: Project? = rootProject.subprojects.find { "testplugins" == it.name } @@ -36,6 +35,8 @@ val gameManagedLibrariesConfig = configurations.register("gameManagedLibraries") val bootShadedLibrariesConfig = configurations.register("bootShadedLibraries") val gameShadedLibrariesConfig = configurations.register("gameShadedLibraries") +val testModulesConfig = configurations.register("testModules") + // ModLauncher layers val bootLayerConfig = configurations.register("bootLayer") { extendsFrom(bootLibrariesConfig.get()) @@ -128,6 +129,10 @@ val testSources = sourceSets.named("test") { spongeImpl.addDependencyToImplementation(bootstrapMain.get(), this) spongeImpl.addDependencyToImplementation(bootstrapForge.get(), this) + + configurations.named(implementationConfigurationName) { + extendsFrom(testModulesConfig.get()) + } } val mixinConfigs = spongeImpl.mixinConfigurations @@ -161,11 +166,6 @@ dependencies { installer(libs.tinylog.api) installer(libs.tinylog.impl) - installer(libs.forgeAutoRenamingTool) { - exclude(group = "net.sf.jopt-simple") - exclude(group = "org.ow2.asm") - } - installer(project(libraryManagerProject.path)) // optional at runtime @@ -197,6 +197,7 @@ dependencies { boot(libs.log4j.core) boot(libs.log4j.slf4j2) + boot(libs.accessWidener) boot(libs.mixin) boot(libs.mixinextras.common) boot(libs.asm.tree) @@ -208,10 +209,6 @@ dependencies { // All minecraft dependencies except itself spongeImpl.copyModulesExcludingPrefix(configurations.minecraft.get(), "net.minecraft", "joined", bootLibrariesConfig.get()) - boot(project(transformersProject.path)) { - exclude(group = "cpw.mods", module = "modlauncher") - } - val game = gameLibrariesConfig.name game("org.spongepowered:spongeapi:$apiVersion") game(libs.javaxInject) @@ -219,9 +216,6 @@ dependencies { game(libs.adventure.serializerConfigurate4) game(libs.adventure.serializerAnsi) - val bootShadedLibraries = bootShadedLibrariesConfig.name - bootShadedLibraries(project(transformersProject.path)) { isTransitive = false } - val gameShadedLibraries = gameShadedLibrariesConfig.name gameShadedLibraries("org.spongepowered:spongeapi:$apiVersion") { isTransitive = false } @@ -237,10 +231,8 @@ dependencies { testImplementation(apiLibs.junit.launcher) testRuntimeOnly(apiLibs.junit.engine) - testImplementation(libs.mockito.core) - testImplementation(libs.mockito.junitJupiter) { - exclude(group = "org.junit.jupiter", module = "junit-jupiter-api") - } + val test = testModulesConfig.name + test(apiLibs.mockito) testRuntimeOnly(libs.jacoco.core) { exclude(group = "org.ow2.asm") @@ -326,8 +318,9 @@ appLaunch.apply { main.apply { blossom.resources { property("apiVersion", apiVersion) - property("minecraftVersion", minecraftVersion) property("version", version.toString()) + property("description", description.toString()) + property("minecraftVersion", minecraftVersion) } } installer.apply { @@ -483,6 +476,8 @@ tasks { val runServer = minecraft.runs.server().get() jvmArgs(runServer.allJvmArguments()) + jvmArgs("--module-path=" + testModulesConfig.get().asPath) + jvmArgs("--add-modules=net.bytebuddy.agent,net.bytebuddy") jvmArgs("-Dsponge.test.args=" + runServer.allArguments().joinToString(" ")) jvmArgs("-Dsponge.jacoco.packages=org.spongepowered") jvmArgs("-Djunit.platform.launcher.interceptors.enabled=true") diff --git a/vanilla/gradle.properties b/vanilla/gradle.properties index e34373dcd39..80fb2390f88 100644 --- a/vanilla/gradle.properties +++ b/vanilla/gradle.properties @@ -2,4 +2,4 @@ name=SpongeVanilla implementation=Vanilla description=The SpongeAPI implementation for Vanilla Minecraft -mixinConfigs=mixins.spongevanilla.accessors.json,mixins.spongevanilla.api.json,mixins.spongevanilla.core.json,mixins.spongevanilla.core.shared.json,mixins.spongevanilla.inventory.json,mixins.spongevanilla.inventory.shared.json,mixins.spongevanilla.tracker.json \ No newline at end of file +mixinConfigs=mixins.spongevanilla.accessors.json,mixins.spongevanilla.api.json,mixins.spongevanilla.core.json,mixins.spongevanilla.core.shared.json,mixins.spongevanilla.inventory.json,mixins.spongevanilla.inventory.shared.json,mixins.spongevanilla.tracker.json diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/JavaPluginLanguageService.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/JavaPluginLanguageService.java deleted file mode 100644 index 07b026699e3..00000000000 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/JavaPluginLanguageService.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.vanilla.applaunch.plugin; - -import org.spongepowered.plugin.Environment; -import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.PluginResource; -import org.spongepowered.plugin.builtin.StandardPluginCandidate; -import org.spongepowered.plugin.builtin.StandardPluginLanguageService; -import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; -import org.spongepowered.plugin.metadata.PluginMetadata; - -import java.io.InputStream; -import java.nio.file.Files; -import java.util.LinkedList; -import java.util.List; - -public final class JavaPluginLanguageService extends StandardPluginLanguageService { - private final static String NAME = "java_plain"; - - @Override - public String name() { - return JavaPluginLanguageService.NAME; - } - - @Override - public String pluginLoader() { - return "org.spongepowered.vanilla.launch.plugin.JavaPluginLoader"; - } - - @Override - public List createPluginCandidates(final Environment environment, final PluginResource resource) throws Exception { - if (resource instanceof JVMPluginResource jvmResource && Files.exists(jvmResource.resourcesRoot().resolve("net/minecraft/server/MinecraftServer.class"))) { - this.logger.debug("Container in path '{}' has been detected as Minecraft.", resource.path()); - - final List candidates = new LinkedList<>(); - try (final InputStream stream = JavaPluginLanguageService.class.getClassLoader().getResourceAsStream("META-INF/minecraft_sponge_plugins.json")) { - for (final PluginMetadata metadata : loadMetadataContainer(environment, stream).metadata()) { - candidates.add(new StandardPluginCandidate(metadata, resource)); - } - } - - return candidates; - } - - return super.createPluginCandidates(environment, resource); - } -} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaPluginPlatform.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaPluginPlatform.java index a67bd50caaa..af2e3f2712f 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaPluginPlatform.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaPluginPlatform.java @@ -25,87 +25,26 @@ package org.spongepowered.vanilla.applaunch.plugin; import cpw.mods.modlauncher.Launcher; -import cpw.mods.modlauncher.api.IModuleLayerManager; -import org.apache.logging.log4j.Logger; -import org.spongepowered.common.applaunch.config.LaunchConfig; -import org.spongepowered.common.applaunch.config.TokenReplacement; +import cpw.mods.modlauncher.api.IEnvironment; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spongepowered.common.applaunch.plugin.PluginPlatform; -import org.spongepowered.common.applaunch.plugin.PluginPlatformConstants; -import org.spongepowered.plugin.Environment; -import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.PluginLanguageService; -import org.spongepowered.plugin.PluginResource; -import org.spongepowered.plugin.PluginResourceLocatorService; -import org.spongepowered.plugin.blackboard.Blackboard; -import org.spongepowered.plugin.blackboard.Keys; -import org.spongepowered.plugin.builtin.StandardEnvironment; -import org.spongepowered.plugin.builtin.jvm.JVMKeys; -import org.spongepowered.vanilla.applaunch.plugin.resource.SecureJarPluginResource; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import org.spongepowered.vanilla.applaunch.plugin.discovery.SecureJarPluginResource; +import org.spongepowered.vanilla.applaunch.plugin.discovery.VanillaPluginDiscovery; -import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.LinkedList; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.ServiceConfigurationError; -import java.util.ServiceLoader; -import java.util.Set; -public final class VanillaPluginPlatform implements PluginPlatform { +public final class VanillaPluginPlatform extends PluginPlatform { + private static final List closeCallbacks = new ArrayList<>(); - private final Environment environment; - private final LaunchConfig config; - private final TokenReplacement tokens; + private final VanillaPluginDiscovery discovery; + private @MonotonicNonNull Path baseDirectory; - private final Map> locatorServices; - private final Map languageServices; - - private final Map> locatorResources; - private final Map> pluginCandidates; - - public VanillaPluginPlatform(final Path baseDirectory) throws IOException { - this.environment = new StandardEnvironment(); - - final String implVersion = StandardEnvironment.class.getPackage().getImplementationVersion(); - this.setVersion(implVersion == null ? "dev" : implVersion); - - this.setBaseDirectory(baseDirectory); - this.setMetadataFilePath(PluginPlatformConstants.METADATA_FILE_LOCATION); - - this.config = LaunchConfig.load(baseDirectory, false); - - final Path modsDirectory = baseDirectory.resolve("mods"); - this.tokens = new TokenReplacement(); - this.tokens.register("BASE_DIR", baseDirectory); - this.tokens.register("CONFIG_DIR", this.configDirectory()); - this.tokens.register("MODS_DIR", modsDirectory); - - this.locatorServices = new HashMap<>(); - this.languageServices = new HashMap<>(); - this.locatorResources = new HashMap<>(); - this.pluginCandidates = new IdentityHashMap<>(); - - final Path additionalPluginsDirectory = Path.of(this.tokens.replace(this.config.additionalPluginsDirectory())); - Files.createDirectories(additionalPluginsDirectory); - this.setPluginDirectories(List.of(modsDirectory, additionalPluginsDirectory)); - } - - @Override - public String version() { - return this.environment.blackboard().get(Keys.VERSION); - } - - private void setVersion(final String version) { - this.environment.blackboard().set(Keys.VERSION, version); - } - - @Override - public Logger logger() { - return this.environment.logger(); + public VanillaPluginPlatform() { + this.discovery = new VanillaPluginDiscovery(this.environment()); } @Override @@ -115,7 +54,10 @@ public boolean vanilla() { @Override public Path baseDirectory() { - return this.environment.blackboard().get(Keys.BASE_DIRECTORY); + if (this.baseDirectory == null) { + this.baseDirectory = Launcher.INSTANCE.environment().getProperty(IEnvironment.Keys.GAMEDIR.get()).orElse(Paths.get(".")); + } + return this.baseDirectory; } @Override @@ -124,135 +66,34 @@ public Path configDirectory() { } @Override - public LaunchConfig config() { - return this.config; + public Path modsDirectory() { + return this.baseDirectory().resolve("mods"); } @Override - public TokenReplacement tokens() { - return this.tokens; - } - - private void setBaseDirectory(final Path baseDirectory) { - this.environment.blackboard().set(Keys.BASE_DIRECTORY, baseDirectory); + public VanillaPluginDiscovery discovery() { + return this.discovery; } @Override - public List pluginDirectories() { - return this.environment.blackboard().get(Keys.PLUGIN_DIRECTORIES); - } - - private void setPluginDirectories(final List pluginDirectories) { - this.environment.blackboard().set(Keys.PLUGIN_DIRECTORIES, pluginDirectories); - } - - public String metadataFilePath() { - return this.environment.blackboard().get(Keys.METADATA_FILE_PATH); + public JVMPluginResource create(final Path[] paths) { + return new SecureJarPluginResource(paths); } - private void setMetadataFilePath(final String metadataFilePath) { - this.environment.blackboard().set(Keys.METADATA_FILE_PATH, metadataFilePath); - } - - public Environment getEnvironment() { - return this.environment; - } - - public Map> getLocatorServices() { - return Collections.unmodifiableMap(this.locatorServices); - } - - public Map getLanguageServices() { - return Collections.unmodifiableMap(this.languageServices); - } - - public Map> getResources() { - return Collections.unmodifiableMap(this.locatorResources); - } - - public Map> getCandidates() { - return Collections.unmodifiableMap(this.pluginCandidates); - } - - public void initializeLanguageServices() { - for (final Map.Entry entry : this.languageServices.entrySet()) { - entry.getValue().initialize(this.environment); - } - } - - public void discoverLocatorServices() { - final Blackboard blackboard = this.environment.blackboard(); - blackboard.set(JVMKeys.ENVIRONMENT_LOCATOR_VARIABLE_NAME, "SPONGE_PLUGINS"); - blackboard.set(JVMKeys.JVM_PLUGIN_RESOURCE_FACTORY, SecureJarPluginResource::new); - - final ModuleLayer serviceLayer = Launcher.INSTANCE.environment().findModuleLayerManager().flatMap(lm -> lm.getLayer(IModuleLayerManager.Layer.SERVICE)).orElseThrow(); - final var serviceLoader = ServiceLoader.load(serviceLayer, PluginResourceLocatorService.class); - - for (final var iter = serviceLoader.iterator(); iter.hasNext(); ) { - final PluginResourceLocatorService next; - - try { - next = iter.next(); - } catch (final ServiceConfigurationError e) { - this.environment.logger().error("Error encountered initializing plugin resource locator!", e); - continue; - } - - this.locatorServices.put(next.name(), next); - } - } - - public void discoverLanguageServices() { - final ModuleLayer pluginLayer = Launcher.INSTANCE.environment().findModuleLayerManager().flatMap(lm -> lm.getLayer(IModuleLayerManager.Layer.PLUGIN)).orElseThrow(); - final var serviceLoader = ServiceLoader.load(pluginLayer, PluginLanguageService.class); - - for (final var iter = serviceLoader.iterator(); iter.hasNext(); ) { - final PluginLanguageService next; - - try { - next = iter.next(); - } catch (final ServiceConfigurationError e) { - this.environment.logger().error("Error encountered initializing plugin language service!", e); - continue; - } - - this.languageServices.put(next.name(), next); - } - } - - public void locatePluginResources() { - for (final Map.Entry> locatorEntry : this.locatorServices.entrySet()) { - final PluginResourceLocatorService locatorService = locatorEntry.getValue(); - final Set resources = locatorService.locatePluginResources(this.environment); - if (!resources.isEmpty()) { - this.locatorResources.put(locatorEntry.getKey(), resources); - } - } + @Override + public void addLoaderCloseCallback(final AutoCloseable closeable) { + VanillaPluginPlatform.closeCallbacks.add(closeable); } - public void createPluginCandidates() { - for (final PluginLanguageService languageService : this.languageServices.values()) { - for (final Set resources : this.locatorResources.values()) { - for (final PluginResource pluginResource : resources) { - if (ResourceType.of(pluginResource) != ResourceType.PLUGIN) { - continue; - } - - try { - final List candidates = languageService.createPluginCandidates(this.environment, pluginResource); - if (candidates.isEmpty()) { - continue; - } - this.pluginCandidates.computeIfAbsent(languageService, k -> new LinkedList<>()).addAll(candidates); - - if (pluginResource instanceof SecureJarPluginResource jarResource) { - jarResource.addCandidates(candidates); - } - } catch (final Exception ex) { - this.environment.logger().error("Failed to create plugin candidates", ex); - } + static { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + for (final AutoCloseable closeCallback : VanillaPluginPlatform.closeCallbacks) { + try { + closeCallback.close(); + } catch (Exception e) { + PluginPlatform.LOGGER.error("Failed to run close callback {}", closeCallback, e); } } - } + }, "Sponge shutdown thread")); } } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaTransformationService.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaTransformationService.java index 69097b21a06..4263f563cbd 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaTransformationService.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/VanillaTransformationService.java @@ -24,174 +24,86 @@ */ package org.spongepowered.vanilla.applaunch.plugin; -import com.google.common.collect.ImmutableList; import cpw.mods.jarhandling.SecureJar; -import cpw.mods.modlauncher.Launcher; import cpw.mods.modlauncher.api.IEnvironment; import cpw.mods.modlauncher.api.IModuleLayerManager; import cpw.mods.modlauncher.api.ITransformationService; import cpw.mods.modlauncher.api.ITransformer; import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.NonNull; import org.fusesource.jansi.AnsiConsole; import org.spongepowered.asm.launch.MixinLaunchPlugin; import org.spongepowered.common.applaunch.AppLaunch; -import org.spongepowered.plugin.PluginResource; -import org.spongepowered.transformers.modlauncher.AccessWidenerTransformationService; -import org.spongepowered.transformers.modlauncher.SuperclassChanger; -import org.spongepowered.vanilla.applaunch.Constants; -import org.spongepowered.vanilla.applaunch.plugin.resource.SecureJarPluginResource; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.vanilla.applaunch.plugin.discovery.SecureJarPluginResource; +import org.spongepowered.vanilla.applaunch.transformation.VanillaAccessWidenerTransformer; +import org.spongepowered.vanilla.applaunch.transformation.VanillaSuperclassChangeTransformer; -import java.io.IOException; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.Set; public final class VanillaTransformationService implements ITransformationService { - private @MonotonicNonNull VanillaPluginPlatform pluginPlatform; + private @MonotonicNonNull VanillaPluginPlatform platform; @Override - public @NonNull String name() { + public String name() { return "spongevanilla"; } @Override public void onLoad(final IEnvironment env, final Set otherServices) { AnsiConsole.systemInstall(); - - final Path baseDirectory = env.getProperty(IEnvironment.Keys.GAMEDIR.get()).orElse(Paths.get(".")); - try { - this.pluginPlatform = new VanillaPluginPlatform(baseDirectory); - } catch (IOException e) { - throw new RuntimeException(e); - } - AppLaunch.setPluginPlatform(this.pluginPlatform); - - this.pluginPlatform.logger().info("SpongePowered PLUGIN Subsystem Version={} Source={}", this.pluginPlatform.version(), this.getCodeSource()); + this.platform = AppLaunch.bootstrap(VanillaPluginPlatform::new); } @Override - public void initialize(final IEnvironment environment) { - this.pluginPlatform.initializeLanguageServices(); - } + public void initialize(final IEnvironment environment) {} @Override - public List beginScanning(final IEnvironment environment) { - this.pluginPlatform.discoverLocatorServices(); - this.pluginPlatform.getLocatorServices().forEach((k, v) -> this.pluginPlatform.logger().info("Plugin resource locator '{}' found.", k)); - - this.pluginPlatform.locatePluginResources(); - - final List languageResources = new ArrayList<>(); - - for (final Set resources : this.pluginPlatform.getResources().values()) { - for (final PluginResource resource : resources) { - if (resource instanceof SecureJarPluginResource secureJarResource) { - if (ResourceType.of(resource) == ResourceType.LANGUAGE) { - languageResources.add(secureJarResource.jar()); - } - } - } - } - - return List.of(new Resource(IModuleLayerManager.Layer.PLUGIN, languageResources)); - } - - @Override - public List completeScan(IModuleLayerManager layerManager) { - this.pluginPlatform.discoverLanguageServices(); - this.pluginPlatform.getLanguageServices().forEach((k, v) -> this.pluginPlatform.logger().info("Plugin language loader '{}' found.", k)); - - this.pluginPlatform.createPluginCandidates(); - - IEnvironment env = Launcher.INSTANCE.environment(); - final AccessWidenerTransformationService accessWidener = env.getProperty(AccessWidenerTransformationService.INSTANCE.get()).orElse(null); - final SuperclassChanger superclassChanger = env.getProperty(SuperclassChanger.INSTANCE.get()).orElse(null); + public List beginScanning(final IEnvironment env) { final ILaunchPluginService mixin = env.findLaunchPlugin(MixinLaunchPlugin.NAME).orElse(null); - final List gameResources = new ArrayList<>(); + this.platform.discovery().discoverPluginResources(); - for (final Set resources : this.pluginPlatform.getResources().values()) { - for (final PluginResource resource : resources) { - if (resource instanceof SecureJarPluginResource secureJarResource) { - // Build jar metadata from first candidate, or fallback to standard - secureJarResource.init(); + final List gameLibs = new ArrayList<>(); - if (ResourceType.of(resource) == ResourceType.PLUGIN) { - gameResources.add(secureJarResource.jar()); - } - } + for (final PluginDiscovery.Candidate candidate : this.platform.discovery().candidates()) { + final PluginResource resource = candidate.resource(); + candidate.readMetadata(); - // Offer jar to the Mixin service - if (mixin != null) { - mixin.offerResource(resource.path(), resource.path().getFileName().toString()); - } + if (candidate.gameResource() && resource instanceof SecureJarPluginResource jarResource) { + gameLibs.add(candidate.pluginFound() ? jarResource.pluginJar(candidate.metadata().getFirst()) : jarResource.jar()); - // Offer jar to the AW service - if (accessWidener != null) { - final Optional awFiles = resource.property(Constants.ManifestAttributes.ACCESS_WIDENER); - if (awFiles.isPresent()) { - for (final String awFile : awFiles.get().split(",")) { - if (!awFile.endsWith(AccessWidenerTransformationService.ACCESS_WIDENER_EXTENSION)) { - continue; - } - try { - accessWidener.offerResource(resource.locateResource(awFile).get().toURL(), awFile); - } catch (final Exception ex) { - this.pluginPlatform.logger().warn("Failed to read declared access widener {}, from {}:", awFile, resource.locator()); - } - } - } - } + if (mixin != null) { + final Path path = jarResource.jar().getPrimaryPath(); + mixin.offerResource(path, path.getFileName().toString()); - // Offer jar to the SuperclassChanger service - if (superclassChanger != null) { - final Optional superclassChangeFiles = resource.property(Constants.ManifestAttributes.SUPERCLASS_CHANGE); - if (superclassChangeFiles.isPresent()) { - for (final String superclassChangeFile : superclassChangeFiles.get().split(",")) { - if (!superclassChangeFile.endsWith(SuperclassChanger.SUPER_CLASS_EXTENSION)) { - continue; - } - try { - superclassChanger.offerResource(resource.locateResource(superclassChangeFile).get().toURL(), superclassChangeFile); - } catch (final Exception ex) { - this.pluginPlatform.logger().warn("Failed to read declared superclass changer {}, from {}:", superclassChangeFile, resource.locator()); - } + if (resource.property(org.spongepowered.asm.util.Constants.ManifestAttributes.MIXINCONFIGS).isPresent()) { + if (candidate.metadata().stream().noneMatch(m -> "spongevanilla".equals(m.id()))) { + this.platform.logger().warn("Plugin from {} uses Mixins to modify the Minecraft Server. If something breaks, remove it before reporting the problem to Sponge!", path); } } } - - // Log warning about plugin using Mixin - if (mixin != null && resource.property(org.spongepowered.asm.util.Constants.ManifestAttributes.MIXINCONFIGS).isPresent()) { - if (!VanillaTransformationService.isSponge(resource)) { - this.pluginPlatform.logger().warn("Plugin from {} uses Mixins to modify the Minecraft Server. If something breaks, remove it before reporting the problem to Sponge!", resource.path()); - } - } } + + candidate.logResult(); } - return List.of(new Resource(IModuleLayerManager.Layer.GAME, gameResources)); - } + this.platform.discovery().logMetadataWarnings(); - private static boolean isSponge(final PluginResource resource) { - return resource instanceof SecureJarPluginResource secureJarResource && secureJarResource.jar().name().equals("spongevanilla"); - } + if (this.platform.discovery().checkConflicts()) { + throw new IllegalStateException("Fatal plugin conflicts have been detected."); + } - @Override - @SuppressWarnings("rawtypes") - public @NonNull List transformers() { - return ImmutableList.of(); + return List.of(new Resource(IModuleLayerManager.Layer.GAME, gameLibs)); } - private String getCodeSource() { - try { - return this.getClass().getProtectionDomain().getCodeSource().getLocation().toString(); - } catch (final Throwable th) { - return "Unknown"; - } + @SuppressWarnings("rawtypes") + @Override + public List transformers() { + return List.of(new VanillaAccessWidenerTransformer(this.platform.discovery()), new VanillaSuperclassChangeTransformer(this.platform.discovery())); } } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/MinecraftMetadataReader.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/MinecraftMetadataReader.java new file mode 100644 index 00000000000..d6149e0d015 --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/MinecraftMetadataReader.java @@ -0,0 +1,66 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.plugin.discovery; + +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import org.spongepowered.plugin.discovery.PluginMetadataReader; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.metadata.builtin.MetadataParser; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.List; + +public final class MinecraftMetadataReader implements PluginMetadataReader { + + @Override + public String name() { + return "minecraft"; + } + + @Override + public Collection readPluginMetadata(final Environment environment, final PluginResource resource, final List locators) throws Exception { + if (resource instanceof JVMPluginResource && resource.locate("net/minecraft/server/MinecraftServer.class").isPresent()) { + environment.logger().debug("{} has been detected as Minecraft.", resource); + + final InputStream input = MinecraftMetadataReader.class.getClassLoader().getResourceAsStream("META-INF/minecraft_sponge_plugins.json"); + if (input == null) { + throw new IOException("Unable to locate Minecraft plugins metadata file"); + } + + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { + return MetadataParser.read(reader).plugins(); + } + } + return List.of(); + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/PluginJarMetadata.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/PluginJarMetadata.java new file mode 100644 index 00000000000..523de9fdb0e --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/PluginJarMetadata.java @@ -0,0 +1,80 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.plugin.discovery; + +import cpw.mods.jarhandling.JarMetadata; +import cpw.mods.jarhandling.SecureJar; +import org.spongepowered.plugin.metadata.PluginMetadata; + +import java.lang.module.ModuleDescriptor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; + +public class PluginJarMetadata implements JarMetadata { + private final ModuleDescriptor descriptor; + + public PluginJarMetadata(final SecureJar jar, final Path[] paths) { + this.descriptor = PluginJarMetadata.standardDescriptor(jar, paths); + } + + public PluginJarMetadata(final SecureJar jar, final PluginMetadata plugin) { + this.descriptor = PluginJarMetadata.pluginDescriptor(jar, plugin); + } + + @Override + public String name() { + return this.descriptor.name(); + } + + @Override + public String version() { + return this.descriptor.rawVersion().orElse(null); + } + + @Override + public ModuleDescriptor descriptor() { + return this.descriptor; + } + + private static ModuleDescriptor pluginDescriptor(final SecureJar jar, final PluginMetadata plugin) { + return PluginJarMetadata.descriptorBuilder(jar, plugin.id()).version(plugin.version().toString()).build(); + } + + private static ModuleDescriptor standardDescriptor(final SecureJar jar, final Path[] paths) { + if (jar.moduleDataProvider().findFile("module-info.class").isEmpty() && Arrays.stream(paths).allMatch(Files::isDirectory)) { + return PluginJarMetadata.descriptorBuilder(jar, jar.getPackages().stream().sorted().findFirst().orElseThrow()).build(); + } + return JarMetadata.from(jar, paths).descriptor(); + } + + private static ModuleDescriptor.Builder descriptorBuilder(final SecureJar jar, final String name) { + final ModuleDescriptor.Builder builder = ModuleDescriptor.newAutomaticModule(name).packages(jar.getPackages()); + jar.getProviders().stream() + .filter(p -> !p.providers().isEmpty()) + .forEach(p -> builder.provides(p.serviceName(), p.providers())); + return builder; + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/SecureJarPluginResource.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/SecureJarPluginResource.java similarity index 53% rename from vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/SecureJarPluginResource.java rename to vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/SecureJarPluginResource.java index 62f42cc0dff..c046d010eb1 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/resource/SecureJarPluginResource.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/SecureJarPluginResource.java @@ -22,81 +22,67 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.applaunch.plugin.resource; +package org.spongepowered.vanilla.applaunch.plugin.discovery; -import cpw.mods.jarhandling.JarMetadata; import cpw.mods.jarhandling.SecureJar; -import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; -import org.spongepowered.vanilla.applaunch.plugin.ResourceType; +import org.spongepowered.common.applaunch.plugin.discovery.SpongeJVMPluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; +import java.io.InputStream; +import java.lang.module.ModuleDescriptor; +import java.net.URI; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.jar.Manifest; -public final class SecureJarPluginResource implements JVMPluginResource { - private final String locator; +public final class SecureJarPluginResource implements SpongeJVMPluginResource { + private final Path[] pathsArray; + private final List paths; private final SecureJar jar; - private PluginJarMetadata pluginJarMetadata; - private List candidates; - - public SecureJarPluginResource(final String locator, final Path[] paths) { - Objects.requireNonNull(locator, "locator"); + public SecureJarPluginResource(final Path[] paths) { Objects.requireNonNull(paths, "paths"); - if (paths.length == 0) { throw new IllegalArgumentException("Need at least one path"); } - - this.locator = locator; - this.jar = SecureJar.from(jar -> { - if (ResourceType.of(jar) == ResourceType.PLUGIN) { - this.pluginJarMetadata = new PluginJarMetadata(jar, paths); - this.candidates = new ArrayList<>(); - return this.pluginJarMetadata; - } - return JarMetadata.from(jar, paths); - }, paths); - } - - @Override - public String locator() { - return this.locator; + this.pathsArray = paths; + this.paths = List.of(paths); + this.jar = SecureJar.from(jar -> new PluginJarMetadata(jar, paths), paths); } public SecureJar jar() { return this.jar; } + public SecureJar pluginJar(final PluginMetadata metadata) { + return SecureJar.from(jar -> new PluginJarMetadata(jar, metadata), this.pathsArray); + } + @Override - public Path path() { - return this.jar.getPrimaryPath(); + public List paths() { + return this.paths; } @Override - public Manifest manifest() { - return this.jar.moduleDataProvider().getManifest(); + public Optional locate(final String path) { + return this.jar.moduleDataProvider().findFile(Objects.requireNonNull(path, "path")); } @Override - public Path resourcesRoot() { - return this.jar.getRootPath(); + public Optional open(final String path) { + return this.jar.moduleDataProvider().open(Objects.requireNonNull(path, "path")); } - public void addCandidates(Collection candidates) { - if (this.candidates != null) { - this.candidates.addAll(candidates); - } + @Override + public Manifest manifest() { + return this.jar.moduleDataProvider().getManifest(); } - public void init() { - if (this.pluginJarMetadata != null) { - this.pluginJarMetadata.init(this.candidates.isEmpty() ? null : this.candidates.get(0).metadata()); - } + @Override + public ModuleDescriptor module() { + return this.jar.moduleDataProvider().descriptor(); } @Override @@ -107,19 +93,16 @@ public boolean equals(Object obj) { if (!(obj instanceof SecureJarPluginResource that)) { return false; } - return this.locator.equals(that.locator) && this.jar.equals(that.jar); + return this.paths.equals(that.paths); } @Override public int hashCode() { - return Objects.hash(this.locator, this.jar); + return this.paths.hashCode(); } @Override public String toString() { - return "SecureJarPluginResource[" + - "locator=" + this.locator + ", " + - "jar=" + this.jar + ']'; + return "SecureJarPluginResource[paths=" + this.paths + "]"; } - } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/SpongeVanillaResourceLocator.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/SpongeVanillaResourceLocator.java new file mode 100644 index 00000000000..d7f0bd87992 --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/SpongeVanillaResourceLocator.java @@ -0,0 +1,66 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.plugin.discovery; + +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.discovery.UnknownResourceStrategy; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public final class SpongeVanillaResourceLocator implements PluginResourceLocator { + + @Override + public String name() { + return "spongevanilla"; + } + + @Override + public Collection locatePluginResources(final Environment environment) throws Exception { + final String fsProp = System.getProperty("sponge.rootJarFS"); + if (fsProp == null) { + return List.of(); + } + final FileSystem fs = FileSystems.getFileSystem(new URI(fsProp)); + final Path spongeMod = newJarInJar(fs.getPath("jars", "spongevanilla-mod.jar")); + return List.of(new Result(JVMPluginResource.create(environment, spongeMod), UnknownResourceStrategy.WARN)); + } + + private static Path newJarInJar(final Path jar) throws IOException, URISyntaxException { + URI jij = new URI("jij:" + jar.toAbsolutePath().toUri().getRawSchemeSpecificPart()).normalize(); + final Map env = Map.of("packagePath", jar); + FileSystem jijFS = FileSystems.newFileSystem(jij, env); + return jijFS.getPath("/"); // root of the archive to load + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/VanillaPluginDiscovery.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/VanillaPluginDiscovery.java new file mode 100644 index 00000000000..98bd937d291 --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/discovery/VanillaPluginDiscovery.java @@ -0,0 +1,77 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.plugin.discovery; + +import cpw.mods.jarhandling.SecureJar; +import net.minecraftforge.securemodules.SecureModuleClassLoader; +import net.minecraftforge.securemodules.SecureModuleFinder; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.common.applaunch.plugin.discovery.SpongeJVMPluginResource; +import org.spongepowered.plugin.Environment; + +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +public class VanillaPluginDiscovery extends PluginDiscovery { + private ModuleLayer currentLayer; + private ClassLoader currentLoader; + + public VanillaPluginDiscovery(Environment environment) { + super(environment); + this.currentLayer = getClass().getModule().getLayer(); + this.currentLoader = getClass().getClassLoader(); + } + + @Override + protected ServiceLoader newServiceLoader(final Class serviceClass) { + return ServiceLoader.load(this.currentLayer, serviceClass); + } + + @Override + protected void appendDiscoveryServices(final List resources, final int batch) { + final ModuleLayer parentLayer = this.currentLayer; + final ClassLoader parentLoader = this.currentLoader; + + final List jars = new ArrayList<>(); + final List moduleNames = new ArrayList<>(); + + for (final SpongeJVMPluginResource resource : resources) { + final SecureJar jar = ((SecureJarPluginResource) resource).jar(); + jars.add(jar); + moduleNames.add(jar.name()); + } + + final Configuration config = parentLayer.configuration().resolveAndBind(SecureModuleFinder.of(jars), ModuleFinder.of(), moduleNames); + final ClassLoader loader = new SecureModuleClassLoader("SPONGE-DISCOVERY-BATCH-" + batch, null, config, List.of(parentLayer), List.of(parentLoader)); + + this.currentLayer = this.currentLayer.defineModulesWithOneLoader(config, loader); + this.currentLoader = loader; + + this.environment.logger().debug("Built new service layer {} on top of {}.", loader.getName(), parentLoader.getName()); + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/JacocoPluginService.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/JacocoPluginService.java new file mode 100644 index 00000000000..99bc2142157 --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/JacocoPluginService.java @@ -0,0 +1,62 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.transformation; + +import cpw.mods.modlauncher.TransformingClassLoader; +import cpw.mods.modlauncher.api.ITransformerActivity; +import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.common.applaunch.transformation.jacoco.JacocoTransformer; + +import java.util.EnumSet; + +public class JacocoPluginService extends JacocoTransformer implements ILaunchPluginService { + private static final EnumSet YAY = EnumSet.of(Phase.BEFORE); + private static final EnumSet NAY = EnumSet.noneOf(Phase.class); + + @Override + public String name() { + return JacocoTransformer.NAME; + } + + @Override + protected boolean isTransformingClassloader(ClassLoader loader) { + return loader instanceof TransformingClassLoader; + } + + @Override + public EnumSet handlesClass(Type classType, boolean isEmpty) { + return this.disabled || isEmpty ? NAY : YAY; + } + + @Override + public int processClassWithFlags(final Phase phase, final ClassNode classNode, final Type classType, final String reason) { + if (ITransformerActivity.COMPUTING_FRAMES_REASON.equals(reason)) { + return ComputeFlags.NO_REWRITE; + } + return this.transform(classNode) ? ComputeFlags.SIMPLE_REWRITE : ComputeFlags.NO_REWRITE; + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ListenerPluginService.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/ListenerPluginService.java similarity index 84% rename from vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ListenerPluginService.java rename to vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/ListenerPluginService.java index 6953b18551c..9c718f44359 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/plugin/ListenerPluginService.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/ListenerPluginService.java @@ -22,22 +22,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.applaunch.plugin; +package org.spongepowered.vanilla.applaunch.transformation; import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; -import org.spongepowered.transformers.modlauncher.ListenerTransformerHelper; +import org.spongepowered.common.applaunch.transformation.ListenerTransformer; import java.util.EnumSet; -public class ListenerPluginService implements ILaunchPluginService { +public class ListenerPluginService extends ListenerTransformer implements ILaunchPluginService { private static final EnumSet YAY = EnumSet.of(Phase.AFTER); private static final EnumSet NAY = EnumSet.noneOf(Phase.class); @Override public String name() { - return "listener"; + return ListenerTransformer.NAME; } @Override @@ -47,8 +47,8 @@ public EnumSet handlesClass(Type classType, boolean isEmpty) { @Override public int processClassWithFlags(final Phase phase, final ClassNode classNode, final Type classType, final String reason) { - if (ListenerTransformerHelper.shouldTransform(classNode)) { - ListenerTransformerHelper.transform(classNode); + if (this.shouldTransform(classNode)) { + this.transform(classNode); return ComputeFlags.COMPUTE_FRAMES; } return ComputeFlags.NO_REWRITE; diff --git a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/ListenerTransformer.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/VanillaAccessWidenerTransformer.java similarity index 54% rename from neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/ListenerTransformer.java rename to vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/VanillaAccessWidenerTransformer.java index d2e116316c6..3e1744a7c6f 100644 --- a/neoforge/src/applaunch/java/org/spongepowered/neoforge/applaunch/transformation/ListenerTransformer.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/VanillaAccessWidenerTransformer.java @@ -22,63 +22,65 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge.applaunch.transformation; +package org.spongepowered.vanilla.applaunch.transformation; import cpw.mods.modlauncher.api.ITransformer; import cpw.mods.modlauncher.api.ITransformerActivity; import cpw.mods.modlauncher.api.ITransformerVotingContext; -import cpw.mods.modlauncher.api.TargetType; import cpw.mods.modlauncher.api.TransformerVoteResult; -import net.neoforged.fml.loading.LoadingModList; -import net.neoforged.fml.loading.moddiscovery.ModFileInfo; -import net.neoforged.neoforgespi.language.ModFileScanData; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; -import org.spongepowered.transformers.modlauncher.ListenerTransformerHelper; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.common.applaunch.transformation.AccessWidenerTransformer; +import org.spongepowered.plugin.discovery.PluginResource; -import java.util.HashSet; -import java.util.Set; +import java.net.URL; +import java.util.*; +import java.util.stream.Collectors; -public class ListenerTransformer implements ITransformer { +public class VanillaAccessWidenerTransformer extends AccessWidenerTransformer implements ITransformer { + private static final String[] LABELS = { AccessWidenerTransformer.NAME }; + + private final PluginDiscovery discovery; + + public VanillaAccessWidenerTransformer(final PluginDiscovery discovery) { + this.discovery = discovery; + } + + @Override + public String[] labels() { + return LABELS; + } - @NonNull @Override public ClassNode transform(final ClassNode input, final ITransformerVotingContext context) { - ListenerTransformerHelper.transform(input); - return input; + return this.transform(input); } - @NonNull @Override public TransformerVoteResult castVote(final ITransformerVotingContext context) { return context.getReason().equals(ITransformerActivity.CLASSLOADING_REASON) ? TransformerVoteResult.YES : TransformerVoteResult.NO; } - @NonNull @Override - public Set> targets() { - final Type listenerType = Type.getType(ListenerTransformerHelper.LISTENER_DESC); + public Set targets() { + return this.targetClasses().stream().map(Target::targetClass).collect(Collectors.toSet()); + } - final Set listenerClasses = new HashSet<>(); - for (ModFileInfo fileInfo : LoadingModList.get().getModFiles()) { - for (ModFileScanData.AnnotationData annotation : fileInfo.getFile().getScanResult().getAnnotations()) { - if (listenerType.equals(annotation.annotationType())) { - listenerClasses.add(annotation.clazz()); + @Override + protected Collection collectResources() { + final Collection resources = new ArrayList<>(); + for (final PluginResource plugin : this.discovery.gameResources()) { + final Optional attribute = plugin.property(AccessWidenerTransformer.MANIFEST_ATTRIBUTE); + if (attribute.isPresent()) { + for (final String path : attribute.get().split(",")) { + try { + resources.add(plugin.locate(path).get().toURL()); + } catch (final Exception e) { + LOGGER.warn("Failed to locate access widener {} from {}", path, plugin, e); + } } } } - - final Set> targets = new HashSet<>(); - for (Type listener : listenerClasses) { - targets.add(Target.targetClass(listener.getInternalName())); - } - return targets; - } - - @NonNull - @Override - public TargetType getTargetType() { - return TargetType.CLASS; + return resources; } } diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/VanillaSuperclassChangeTransformer.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/VanillaSuperclassChangeTransformer.java new file mode 100644 index 00000000000..954a4255768 --- /dev/null +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/transformation/VanillaSuperclassChangeTransformer.java @@ -0,0 +1,91 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.applaunch.transformation; + +import cpw.mods.modlauncher.api.ITransformer; +import cpw.mods.modlauncher.api.ITransformerActivity; +import cpw.mods.modlauncher.api.ITransformerVotingContext; +import cpw.mods.modlauncher.api.TransformerVoteResult; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; +import org.spongepowered.common.applaunch.transformation.SuperclassChangeTransformer; +import org.spongepowered.plugin.discovery.PluginResource; + +import java.net.URL; +import java.util.*; +import java.util.stream.Collectors; + +public class VanillaSuperclassChangeTransformer extends SuperclassChangeTransformer implements ITransformer { + private static final String MIXIN_PLUGIN_REASON = "mixin"; + private static final String[] LABELS = { SuperclassChangeTransformer.NAME }; + + private final PluginDiscovery discovery; + + public VanillaSuperclassChangeTransformer(final PluginDiscovery discovery) { + this.discovery = discovery; + } + + @Override + public String[] labels() { + return LABELS; + } + + @Override + public ClassNode transform(final ClassNode input, final ITransformerVotingContext context) { + this.transform(input); + return input; + } + + @Override + public TransformerVoteResult castVote(final ITransformerVotingContext context) { + return switch (context.getReason()) { + case ITransformerActivity.CLASSLOADING_REASON, VanillaSuperclassChangeTransformer.MIXIN_PLUGIN_REASON -> TransformerVoteResult.YES; + default -> TransformerVoteResult.NO; + }; + } + + @Override + public Set targets() { + return this.targetClasses().stream().map(Target::targetClass).collect(Collectors.toSet()); + } + + @Override + protected Collection collectResources() { + final Collection resources = new ArrayList<>(); + for (final PluginResource plugin : this.discovery.gameResources()) { + final Optional attribute = plugin.property(SuperclassChangeTransformer.MANIFEST_ATTRIBUTE); + if (attribute.isPresent()) { + for (final String scPath : attribute.get().split(",")) { + try { + resources.add(plugin.locate(scPath).get().toURL()); + } catch (final Exception e) { + LOGGER.warn("Failed to locate superclass changer {} from {}", scPath, plugin); + } + } + } + } + return resources; + } +} diff --git a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/util/CaptureAppender.java b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/util/CaptureAppender.java index 94d7546b2e9..3b94f2c4ca9 100644 --- a/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/util/CaptureAppender.java +++ b/vanilla/src/applaunch/java/org/spongepowered/vanilla/applaunch/util/CaptureAppender.java @@ -40,7 +40,7 @@ public class CaptureAppender extends AbstractAppender { private final Queue messages = new ConcurrentLinkedQueue<>(); - public static Builder builder() { + public static CaptureAppender.Builder builder() { return new Builder(); } @@ -63,7 +63,7 @@ public Queue messages() { return this.messages; } - public static class Builder extends AbstractAppender.Builder { + public static class Builder extends AbstractAppender.Builder { Builder() { } diff --git a/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService b/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService index fcea6e880a6..b17475d9e1c 100644 --- a/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService +++ b/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService @@ -1 +1 @@ -org.spongepowered.vanilla.applaunch.plugin.VanillaTransformationService \ No newline at end of file +org.spongepowered.vanilla.applaunch.plugin.VanillaTransformationService diff --git a/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService b/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService index 819258d57e1..9abe0b2f9b2 100644 --- a/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService +++ b/vanilla/src/applaunch/resources/META-INF/services/cpw.mods.modlauncher.serviceapi.ILaunchPluginService @@ -1 +1,2 @@ -org.spongepowered.vanilla.applaunch.plugin.ListenerPluginService \ No newline at end of file +org.spongepowered.vanilla.applaunch.transformation.JacocoPluginService +org.spongepowered.vanilla.applaunch.transformation.ListenerPluginService diff --git a/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginLanguageService b/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginLanguageService deleted file mode 100644 index 3e6983f38f8..00000000000 --- a/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginLanguageService +++ /dev/null @@ -1 +0,0 @@ -org.spongepowered.vanilla.applaunch.plugin.JavaPluginLanguageService \ No newline at end of file diff --git a/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginResourceLocatorService b/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginResourceLocatorService deleted file mode 100644 index cf8cd29b6e7..00000000000 --- a/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.PluginResourceLocatorService +++ /dev/null @@ -1 +0,0 @@ -org.spongepowered.vanilla.applaunch.plugin.locator.SpongePropertyLocatorService \ No newline at end of file diff --git a/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginMetadataReader b/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginMetadataReader new file mode 100644 index 00000000000..62c4d04f71e --- /dev/null +++ b/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginMetadataReader @@ -0,0 +1 @@ +org.spongepowered.vanilla.applaunch.plugin.discovery.MinecraftMetadataReader diff --git a/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator b/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator new file mode 100644 index 00000000000..8bac9b4cf51 --- /dev/null +++ b/vanilla/src/applaunch/resources/META-INF/services/org.spongepowered.plugin.discovery.PluginResourceLocator @@ -0,0 +1,2 @@ +org.spongepowered.vanilla.applaunch.plugin.discovery.SpongeVanillaResourceLocator +org.spongepowered.common.applaunch.plugin.discovery.SpongePropertyResourceLocator diff --git a/vanilla/src/applaunch/resources/log4j2.xml b/vanilla/src/applaunch/resources/log4j2.xml index 876e95b0b2e..67a69800e10 100644 --- a/vanilla/src/applaunch/resources/log4j2.xml +++ b/vanilla/src/applaunch/resources/log4j2.xml @@ -1,7 +1,6 @@ - StyleMapName=Spock Name=reset,red NameMessageSeparator=red Message=red StackTraceElement.ClassName=red StackTraceElement.ClassMethodSeparator=red,faint StackTraceElement.MethodName=yellow StackTraceElement.NativeMethod=yellow jdk.internal,sun.reflect @@ -9,10 +8,10 @@ + defaultPattern="[%d{HH:mm:ss}] [%t/%highlight{%level}] [%c{1.3}%notEmpty{/%markerSimpleName}]: %highlightError{%msg{nolookups}%n%xEx{filters(${sponge.exceptionFiltered})}}"> + pattern="[%d{HH:mm:ss}] [%t/%highlight{%level}]: %highlightError{%msg{nolookups}%n%xEx{filters(${sponge.exceptionFiltered})}}"/> diff --git a/vanilla/src/applaunch/resources/log4j2_dev.xml b/vanilla/src/applaunch/resources/log4j2_dev.xml index a3cff500bda..b38cd867887 100644 --- a/vanilla/src/applaunch/resources/log4j2_dev.xml +++ b/vanilla/src/applaunch/resources/log4j2_dev.xml @@ -1,7 +1,6 @@ - StyleMapName=Spock Name=reset,red NameMessageSeparator=red Message=red StackTraceElement.ClassName=red StackTraceElement.ClassMethodSeparator=red,faint StackTraceElement.MethodName=yellow StackTraceElement.NativeMethod=yellow @@ -9,10 +8,10 @@ + defaultPattern="[%d{HH:mm:ss}] [%t/%highlight{%level}] [%c{1.3}%notEmpty{/%markerSimpleName}]: %highlightError{%msg{nolookups}%n%xEx{filters(${sponge.exceptionFiltered})}}"> + pattern="[%d{HH:mm:ss}] [%t/%highlight{%level}]: %highlightError{%msg{nolookups}%n%xEx{filters(${sponge.exceptionFiltered})}}"/> diff --git a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java index 3a4cd688cfc..dbe64be9d6d 100644 --- a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java +++ b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/InstallerMain.java @@ -26,11 +26,6 @@ import com.google.gson.Gson; import com.google.gson.stream.JsonReader; -import net.minecraftforge.fart.api.Renamer; -import net.minecraftforge.fart.api.SignatureStripperConfig; -import net.minecraftforge.fart.api.SourceFixerConfig; -import net.minecraftforge.fart.api.Transformer; -import net.minecraftforge.srgutils.IMappingFile; import org.spongepowered.bootstrap.forge.VanillaBootstrap; import org.spongepowered.libs.LibraryManager; import org.spongepowered.libs.LibraryUtils; @@ -47,19 +42,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.UncheckedIOException; import java.net.URI; import java.net.URL; import java.net.URLConnection; -import java.nio.file.AccessDeniedException; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -78,7 +69,6 @@ public final class InstallerMain { private static final String COLLECTION_BOOTSTRAP = "bootstrap"; // boot layer private static final String COLLECTION_MAIN = "main"; // game layer - private static final int MAX_TRIES = 2; private final Installer installer; private final boolean isolated; @@ -105,43 +95,32 @@ public void run() throws Exception { } private void downloadAndRun() throws Exception { - ServerAndLibraries remappedMinecraftJar = null; + ServerAndLibraries extractedMinecraftJar = null; Version mcVersion = null; try { mcVersion = this.downloadMinecraftManifest(); } catch (final IOException ex) { - remappedMinecraftJar = this.recoverFromMinecraftDownloadError(ex); + extractedMinecraftJar = this.recoverFromMinecraftDownloadError(ex); } final LibraryManager libraryManager = this.installer.getLibraryManager(); try { if (mcVersion != null) { - final CompletableFuture mappingsFuture = this.downloadMappings(mcVersion); - final CompletableFuture originalMcFuture = this.downloadMinecraft(mcVersion); - final CompletableFuture extractedFuture = originalMcFuture - .thenApplyAsync(this::extractBundle, libraryManager.preparationWorker()); - final CompletableFuture remappedMinecraftJarFuture = mappingsFuture.thenCombineAsync(extractedFuture, (mappings, minecraft) -> { - try { - return this.remapMinecraft(minecraft, mappings); - } catch (final IOException ex) { - throw new UncheckedIOException(ex); - } - }, libraryManager.preparationWorker()); - remappedMinecraftJar = remappedMinecraftJarFuture.get(); + extractedMinecraftJar = this.downloadMinecraft(mcVersion).thenApplyAsync(this::extractBundle, libraryManager.preparationWorker()).get(); } } catch (final ExecutionException ex) { final /* @Nullable */ Throwable cause = ex.getCause(); - remappedMinecraftJar = this.recoverFromMinecraftDownloadError(cause instanceof Exception ? (Exception) cause : ex); + extractedMinecraftJar = this.recoverFromMinecraftDownloadError(cause instanceof Exception ? (Exception) cause : ex); } - assert remappedMinecraftJar != null; // always assigned or thrown + assert extractedMinecraftJar != null; // always assigned or thrown libraryManager.validate(); // Minecraft itself is on the main layer - libraryManager.addLibrary(InstallerMain.COLLECTION_MAIN, new LibraryManager.Library("minecraft", remappedMinecraftJar.server())); + libraryManager.addLibrary(InstallerMain.COLLECTION_MAIN, new LibraryManager.Library("minecraft", extractedMinecraftJar.server())); // Other libs are on the bootstrap layer - for (final Map.Entry entry : remappedMinecraftJar.libraries().entrySet()) { + for (final Map.Entry entry : extractedMinecraftJar.libraries().entrySet()) { final GroupArtifactVersion artifact = entry.getKey(); final Path path = entry.getValue(); @@ -220,10 +199,9 @@ private static Path newJarInJar(final Path jar) { private ServerAndLibraries recoverFromMinecraftDownloadError(final T ex) throws T { final Path expectedUnpacked = this.expectedMinecraftLocation(Constants.Libraries.MINECRAFT_VERSION_TARGET); - final Path expectedRemapped = this.expectedRemappedLocation(expectedUnpacked); // Re-read bundler metadata (needs original bundled location) - if (Files.exists(expectedRemapped)) { - Logger.warn(ex, "Failed to download and remap Minecraft. An existing jar exists, so we will attempt to use that instead."); + if (Files.exists(expectedUnpacked)) { + Logger.warn(ex, "Failed to download Minecraft. An existing jar exists, so we will attempt to use that instead."); return this.extractBundle(this.expectedBundleLocation(expectedUnpacked)); } else { throw ex; @@ -285,10 +263,6 @@ private Path expectedMinecraftLocation(final String version) { .resolve(Constants.Libraries.MINECRAFT_SERVER_JAR_NAME + ".jar"); } - private Path expectedRemappedLocation(final Path originalLocation) { - return originalLocation.resolveSibling(Constants.Libraries.MINECRAFT_SERVER_JAR_NAME + "-remapped.jar"); - } - private Path expectedBundleLocation(final Path originalLocation) { return originalLocation.resolveSibling(Constants.Libraries.MINECRAFT_SERVER_JAR_NAME + "-bundle.jar"); } @@ -381,137 +355,9 @@ private ServerAndLibraries extractBundle(final Path bundleJar) { } } - private CompletableFuture downloadMappings(final Version version) { - return LibraryUtils.asyncFailableFuture(() -> { - Logger.info("Setting up names for Minecraft {}", Constants.Libraries.MINECRAFT_VERSION_TARGET); - final Path downloadTarget = this.installer.getLibraryManager().getRootDirectory().resolve(Constants.Libraries.MINECRAFT_MAPPINGS_PREFIX) - .resolve(Constants.Libraries.MINECRAFT_VERSION_TARGET) - .resolve(Constants.Libraries.MINECRAFT_MAPPINGS_NAME); - - final Version.Downloads.Download mappings = version.downloads().server_mappings(); - if (mappings == null) { - throw new IOException(String.format("Mappings were not included in version manifest for %s", Constants.Libraries.MINECRAFT_VERSION_TARGET)); - } - - final boolean checkHashes = this.installer.getConfig().checkLibraryHashes(); - if (Files.exists(downloadTarget)) { - if (checkHashes) { - Logger.info("Detected existing mappings, verifying hashes..."); - if (LibraryUtils.validateDigest("SHA-1", mappings.sha1(), downloadTarget)) { - Logger.info("Mappings verified!"); - return downloadTarget; - } else { - Logger.error("Checksum verification failed: Expected {}. Deleting cached server mappings file...", mappings.sha1()); - Files.delete(downloadTarget); - } - } else { - return downloadTarget; - } - } - - if (this.installer.getConfig().autoDownloadLibraries()) { - if (checkHashes) { - LibraryUtils.downloadAndVerifyDigest(TinyLogger.INSTANCE, mappings.url(), downloadTarget, "SHA-1", mappings.sha1()); - } else { - LibraryUtils.download(TinyLogger.INSTANCE, mappings.url(), downloadTarget, false); - } - } else { - throw new IOException(String.format("Mappings were not located at '%s' and downloading them has been turned off.", downloadTarget)); - } - - return downloadTarget; - }, this.installer.getLibraryManager().preparationWorker()); - } - - private ServerAndLibraries remapMinecraft(final ServerAndLibraries minecraft, final Path serverMappings) throws IOException { - Logger.info("Checking if we need to remap Minecraft..."); - final Path outputJar = this.expectedRemappedLocation(minecraft.server()); - final Path tempOutput = outputJar.resolveSibling(Constants.Libraries.MINECRAFT_SERVER_JAR_NAME + "_remapped.jar.tmp"); - - if (Files.exists(outputJar)) { - Logger.info("Remapped Minecraft detected, skipping..."); - return minecraft.server(outputJar); - } - - Logger.info("Remapping Minecraft. This may take a while..."); - final IMappingFile mappings = IMappingFile.load(serverMappings.toFile()).reverse(); - - final Renamer.Builder renamerBuilder = Renamer.builder() - .add(Transformer.parameterAnnotationFixerFactory()) - .add(ctx -> { - final Transformer backing = Transformer.renamerFactory(mappings, false).create(ctx); - return new Transformer() { - @Override - public ClassEntry process(final ClassEntry entry) { - final String name = entry.getName(); - if (name.startsWith("it/unimi") - || name.startsWith("com/google") - || name.startsWith("com/mojang/datafixers") - || name.startsWith("com/mojang/brigadier") - || name.startsWith("org/apache")) { - return entry; - } - return backing.process(entry); - } - - @Override - public ManifestEntry process(final ManifestEntry entry) { - return backing.process(entry); - } - - @Override - public ResourceEntry process(final ResourceEntry entry) { - return backing.process(entry); - } - - @Override - public Collection getExtras() { - return backing.getExtras(); - } - }; - }) - .add(Transformer.recordFixerFactory()) - .add(Transformer.parameterAnnotationFixerFactory()) - .add(Transformer.sourceFixerFactory(SourceFixerConfig.JAVA)) - .add(Transformer.signatureStripperFactory(SignatureStripperConfig.ALL)) - .logger(Logger::debug); // quiet - - try (final Renamer ren = renamerBuilder.build()) { - ren.run(minecraft.server.toFile(), tempOutput.toFile()); - } - - // Restore file - try { - Files.move(tempOutput, outputJar, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); - } catch (final AccessDeniedException ex) { - // Sometimes because of file locking this will fail... Let's just try again and hope for the best - // Thanks Windows! - for (int tries = 0; tries < InstallerMain.MAX_TRIES; ++tries) { - // Pause for a bit - try { - Thread.sleep(5 * tries); - Files.move(tempOutput, outputJar, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); - } catch (final AccessDeniedException ex2) { - if (tries == InstallerMain.MAX_TRIES - 1) { - throw ex; - } - } catch (final InterruptedException exInterrupt) { - Thread.currentThread().interrupt(); - throw ex; - } - } - } - - return minecraft.server(outputJar); - } - record ServerAndLibraries(Path server, Map libraries) { ServerAndLibraries { libraries = Map.copyOf(libraries); } - - public ServerAndLibraries server(final Path server) { - return new ServerAndLibraries(server, this.libraries); - } } } diff --git a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/model/mojang/Version.java b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/model/mojang/Version.java index 7c526889d08..78d98fdde5b 100644 --- a/vanilla/src/installer/java/org/spongepowered/vanilla/installer/model/mojang/Version.java +++ b/vanilla/src/installer/java/org/spongepowered/vanilla/installer/model/mojang/Version.java @@ -38,7 +38,7 @@ public record Download(String sha1, int size, URL url) { } } - public record Library(Downloads downloads, String name) { + public record Library(Library.Downloads downloads, String name) { public record Downloads(Artifact artifact, Map classifiers) { public record Artifact(String path, String sha1, int size, URL url) { } diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/chat/ChatFormatter.java b/vanilla/src/main/java/org/spongepowered/vanilla/chat/ChatFormatter.java index 7922090389c..5e85e22e6cb 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/chat/ChatFormatter.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/chat/ChatFormatter.java @@ -73,11 +73,13 @@ public static MutableComponent format(final String s) { final String displayUrl = s.substring(start, end); String url = displayUrl; + final URI toOpen; try { if (new URI(url).getScheme() == null) { url = ChatFormatter.DEFAULT_SCHEME + url; } + toOpen = new URI(url); } catch (final URISyntaxException e) { continue; // Invalid URL so just ignore it } @@ -93,7 +95,7 @@ public static MutableComponent format(final String s) { pos = end; final MutableComponent link = Component.literal(displayUrl); - link.setStyle(link.getStyle().withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url))); + link.setStyle(link.getStyle().withClickEvent(new ClickEvent.OpenUrl(toOpen))); if (result == null) { result = Component.literal(""); diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/chat/ReusableComponentMessageFactory.java b/vanilla/src/main/java/org/spongepowered/vanilla/chat/ReusableComponentMessageFactory.java index b55f5f184e0..c190e50331d 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/chat/ReusableComponentMessageFactory.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/chat/ReusableComponentMessageFactory.java @@ -145,7 +145,7 @@ public String getFormattedMessage() { ComponentMessage.RENDERER.render(((ComponentLike) param).asComponent(), Locale.getDefault()) ); } else if (param instanceof net.minecraft.network.chat.Component) { - return NativeComponentRenderer.apply((Component) param, Locale.getDefault()).getString(); + return NativeComponentRenderer.apply((Component) param, Locale.getDefault(), null).getString(); } return super.getFormattedMessage(); } @@ -160,7 +160,7 @@ public void formatTo(final StringBuilder buffer) { buffer::append ); } else if (param instanceof net.minecraft.network.chat.Component) { - buffer.append(NativeComponentRenderer.apply((Component) param, Locale.getDefault()).getString()); + buffer.append(NativeComponentRenderer.apply((Component) param, Locale.getDefault(), null).getString()); } else { super.formatTo(buffer); } diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/screen/PluginScreen.java b/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/screen/PluginScreen.java index cc1a6ad18f0..7a7c729e042 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/screen/PluginScreen.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/screen/PluginScreen.java @@ -26,7 +26,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -97,17 +97,16 @@ protected void init() { // Add the 'Done' button this.addRenderableWidget(Button.builder(Component.translatable(I18n.get("gui.done")), - b -> Minecraft.getInstance().setScreen(this.previousScreen)) + b -> Minecraft.getInstance().setScreenAndShow(this.previousScreen)) .pos(this.width / 2 - 50, this.height - 40) .size(100, 20) .build()); } @Override - public void render(final GuiGraphics stack, final int p_render_1_, final int p_render_2_, final float p_render_3_) { - this.renderBackground(stack, p_render_1_, p_render_2_, p_render_3_); - super.render(stack, p_render_1_, p_render_2_, p_render_3_); // render the widgets - stack.drawCenteredString(this.font, this.title.getString(), this.width / 2, 8, 16777215); + public void extractRenderState(final GuiGraphicsExtractor graphics, final int mouseX, final int mouseY, final float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + // TODO: drawCenteredString needs GuiGraphicsExtractor rewrite (see .docs/plugin-gui-rewrite-plan.md) } private void generateEntries(final List metadatas) { @@ -115,8 +114,7 @@ private void generateEntries(final List metadatas) { return; } - this.selectionList.children().clear(); - this.selectionList.children().addAll(metadatas.stream() + this.selectionList.replaceEntries(metadatas.stream() .map(metadata -> new PluginSelectionList.Entry(this.selectionList, metadata)) .collect(Collectors.toList())); this.selectionList.setSelected(this.selectionList.children().stream().findFirst().orElse(null)); diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/MetadataPanel.java b/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/MetadataPanel.java index e1eb98debb0..e623f37ba66 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/MetadataPanel.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/MetadataPanel.java @@ -25,15 +25,15 @@ package org.spongepowered.vanilla.client.gui.widget; import com.google.common.collect.Lists; -import com.mojang.blaze3d.vertex.Tesselator; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.narration.NarratedElementType; import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; @@ -44,7 +44,6 @@ import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -92,8 +91,9 @@ public void setMetadata(@Nullable final PluginMetadata metadata) { // Details this.categories.add(new Category("Details") - .addEntry(new Entry("ID", metadata.id())).addEntry(new Entry("Name", metadata.name().orElse(null))) - .addEntry(new Entry("Version", metadata.version().toString())).addEntry(new Entry("Entry", metadata.entrypoint())) + .addEntry(new Entry("ID", metadata.id())) + .addEntry(new Entry("Name", metadata.name().orElse(null))) + .addEntry(new Entry("Version", metadata.version().toString())) .addEntry(new Entry("Description", metadata.description().orElse(null)))); // Contributors @@ -113,9 +113,9 @@ public void setMetadata(@Nullable final PluginMetadata metadata) { // Resources this.categories.add( new Category("Resources") - .addEntry(new Entry("Homepage", metadata.links().homepage().map(URL::toString).orElse(null))) - .addEntry(new Entry("Issues", metadata.links().issues().map(URL::toString).orElse(null))) - .addEntry(new Entry("Source", metadata.links().source().map(URL::toString).orElse(null)))); + .addEntry(new Entry("Homepage", metadata.links().homepage().map(URI::toString).orElse(null))) + .addEntry(new Entry("Issues", metadata.links().issues().map(URI::toString).orElse(null))) + .addEntry(new Entry("Source", metadata.links().source().map(URI::toString).orElse(null)))); // Other this.categories.add(new Category("Other", @@ -213,57 +213,33 @@ protected int getScrollAmount() { } @Override - protected void drawPanel(final GuiGraphics stack, final int entryRight, int relativeY, final Tesselator tess, final int mouseX, + protected void drawPanel(final GuiGraphicsExtractor graphics, final int entryRight, int relativeY, final int mouseX, final int mouseY) { - final int baseX = this.left + 4; - - final Font font = this.minecraft.font; + // TODO: Rewrite rendering for GuiGraphicsExtractor pipeline + // The old GuiGraphics.drawString() API no longer exists on GuiGraphicsExtractor. + // This needs a full rewrite to use the new deferred rendering model. if (this.resizedCategories.isEmpty()) { - final int noResultsWidth = font.width(MetadataPanel.NO_RESULTS); - - stack.drawString(font, - MetadataPanel.NO_RESULTS, - (int) (((float) this.width / 2) + this.left - ((float) noResultsWidth / 2)), - this.top + 10, - 0xFFFFFF - ); return; } - // Iterate and draw categories + final int baseX = this.left + 4; + final Font font = this.minecraft.font; for (final Category category : this.resizedCategories) { - // Skip empty categories if (category.getEntries().size() == 0) { continue; } - - // Draw category name - stack.drawString(font, category.name, baseX, relativeY, 0xFFFFFF); relativeY += this.lineHeight; - // Iterate and draw entries for (final Entry entry : category.getEntries()) { if (entry.value == null) { continue; } - final int levelOffset = entry.level * MetadataPanel.INDENT_SIZE; - final int keyX = baseX + MetadataPanel.INDENT_SIZE + levelOffset; - final int separatorX = keyX + this.maxKeyWidth + 4 - levelOffset; - final int valueX = separatorX + font.width(":") + 4; - - // Only draw key and separator if there is any key present - if (entry.key != null) { - stack.drawString(font, entry.key, keyX, relativeY, 0xFFFFFF); - - if (entry.rawValue != null && !entry.rawValue.isEmpty()) { - stack.drawString(font, ":", separatorX, relativeY, 0xFFFFFF); - } - } - - // Draw the value, and update the value bounds if needed - stack.drawString(font, entry.value, valueX, relativeY, 0xFFFFFF); if (entry.value.getStyle().getClickEvent() != null) { + final int levelOffset = entry.level * MetadataPanel.INDENT_SIZE; + final int keyX = baseX + MetadataPanel.INDENT_SIZE + levelOffset; + final int separatorX = keyX + this.maxKeyWidth + 4 - levelOffset; + final int valueX = separatorX + font.width(":") + 4; entry.valueBounds = new Bounds(valueX, relativeY + 1, valueX + font.width(entry.value), relativeY + this.lineHeight); @@ -276,11 +252,11 @@ protected void drawPanel(final GuiGraphics stack, final int entryRight, int rela } @Override - public boolean mouseClicked(final double mouseX, final double mouseY, final int button) { + public boolean mouseClicked(final MouseButtonEvent event, final boolean repeated) { // Find an entry match for where we clicked final Entry entry = this.getAllEntries().stream() - .filter(e -> e.valueBounds != null && e.valueBounds.isInBounds((int) mouseX, (int) mouseY)) + .filter(e -> e.valueBounds != null && e.valueBounds.isInBounds((int) event.x(), (int) event.y())) .findFirst().orElse(null); if (entry == null) { @@ -289,10 +265,10 @@ public boolean mouseClicked(final double mouseX, final double mouseY, final int final Component component = entry.value; if (component != null) { - this.screen.handleComponentClicked(component.getStyle()); +// this.screen.handleComponentClicked(component.getStyle()); return true; } - return super.mouseClicked(mouseX, mouseY, button); + return super.mouseClicked(event, repeated); } @Override @@ -328,6 +304,7 @@ public static Component newChatWithLinks(final String string, final boolean allo lastEnd = end; String url = string.substring(start, end); final MutableComponent link = Component.literal(url); + final URI toOpen; try { // Add schema so client doesn't crash. @@ -340,8 +317,9 @@ public static Component newChatWithLinks(final String string, final boolean allo } continue; } - url = "http://" + url; + url = "https://" + url; } + toOpen = new URI(url); } catch (final URISyntaxException e) { // Bad syntax bail out! if (ichat == null) { @@ -353,7 +331,7 @@ public static Component newChatWithLinks(final String string, final boolean allo } // Set the click event and append the link. - final ClickEvent click = new ClickEvent(ClickEvent.Action.OPEN_URL, url); + final ClickEvent click = new ClickEvent.OpenUrl(toOpen); link.withStyle(style -> style.withClickEvent(click) .withUnderlined(true) .withColor(ChatFormatting.BLUE)); diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/ScrollPanel.java b/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/ScrollPanel.java index cc70debc8d9..f60f9af5c13 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/ScrollPanel.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/ScrollPanel.java @@ -24,19 +24,12 @@ */ package org.spongepowered.vanilla.client.gui.widget; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.BufferUploader; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.Tesselator; -import com.mojang.blaze3d.vertex.VertexFormat; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Renderable; import net.minecraft.client.gui.components.events.AbstractContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.input.MouseButtonEvent; import java.util.Collections; import java.util.List; @@ -44,6 +37,7 @@ /** * Credit: MinecraftForge * Changes: Minor tweaks, fixed scroll limits able to hit negative + * TODO: Rewrite to use AbstractScrollArea instead of custom scroll logic */ public abstract class ScrollPanel extends AbstractContainerEventHandler implements Renderable { @@ -77,14 +71,7 @@ public ScrollPanel(final Minecraft client, final int width, final int height, fi protected void drawBackground() { } - /** - * Draw anything special on the screen. GL_SCISSOR is enabled for anything that - * is rendered outside of the view box. Do not mess with SCISSOR unless you support this. - * - * @param mouseX - * @param mouseY - */ - protected abstract void drawPanel(final GuiGraphics stack, int entryRight, int relativeY, Tesselator tess, int mouseX, int mouseY); + protected abstract void drawPanel(final GuiGraphicsExtractor graphics, int entryRight, int relativeY, int mouseX, int mouseY); protected boolean clickPanel(final double mouseX, final double mouseY, final int button) { return false; @@ -107,7 +94,7 @@ private void applyScrollLimits() { } @Override - public boolean mouseScrolled(final double mouseX, final double mouseY, final double scroll, final double $$3) { + public boolean mouseScrolled(final double mouseX, final double mouseY, final double scroll, final double scrollY) { if (scroll != 0) { this.scrollDistance += -scroll * this.getScrollAmount(); this.applyScrollLimits(); @@ -126,11 +113,14 @@ public boolean isMouseOver(final double mouseX, final double mouseY) { } @Override - public boolean mouseClicked(final double mouseX, final double mouseY, final int button) { - if (super.mouseClicked(mouseX, mouseY, button)) { + public boolean mouseClicked(final MouseButtonEvent event, final boolean doubleClick) { + if (super.mouseClicked(event, doubleClick)) { return true; } + final var button = event.button(); + final var mouseX = event.x(); + final var mouseY = event.y(); this.scrolling = button == 0 && mouseX >= this.barLeft && mouseX < this.barLeft + this.barWidth; if (this.scrolling) { return true; @@ -143,8 +133,8 @@ public boolean mouseClicked(final double mouseX, final double mouseY, final int } @Override - public boolean mouseReleased(final double p_mouseReleased_1_, final double p_mouseReleased_3_, final int p_mouseReleased_5_) { - if (super.mouseReleased(p_mouseReleased_1_, p_mouseReleased_3_, p_mouseReleased_5_)) { + public boolean mouseReleased(final MouseButtonEvent event) { + if (super.mouseReleased(event)) { return true; } final boolean ret = this.scrolling; @@ -167,7 +157,7 @@ private int getBarHeight() { } @Override - public boolean mouseDragged(final double mouseX, final double mouseY, final int button, final double deltaX, final double deltaY) { + public boolean mouseDragged(final MouseButtonEvent event, final double deltaX, final double deltaY) { if (this.scrolling) { final int maxScroll = this.height - this.getBarHeight(); final double moved = deltaY / maxScroll; @@ -178,71 +168,12 @@ public boolean mouseDragged(final double mouseX, final double mouseY, final int return false; } - @Override - public void render(final GuiGraphics stack, final int mouseX, final int mouseY, final float partialTicks) { + public void extractRenderState(final GuiGraphicsExtractor graphics, final int mouseX, final int mouseY, final float partialTicks) { this.drawBackground(); - - final Tesselator tess = Tesselator.getInstance(); - - final double scale = this.client.getWindow().getGuiScale(); - RenderSystem.enableScissor((int) (this.left * scale), (int) (this.client.getWindow().getHeight() - (this.bottom * scale)), (int) (this.width * scale), - (int) (this.height * scale)); - - RenderSystem.setShader(GameRenderer::getPositionTexColorShader); - RenderSystem.setShaderTexture(0, Screen.MENU_BACKGROUND); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - final float texScale = 32.0F; - BufferBuilder worldr = tess.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); - worldr.addVertex(this.left, this.bottom, 0.0f).setUv(this.left / texScale, (this.bottom + (int) this.scrollDistance) / texScale) - .setColor(0x20, 0x20, 0x20, 0xFF); - worldr.addVertex(this.right, this.bottom, 0.0f).setUv(this.right / texScale, (this.bottom + (int) this.scrollDistance) / texScale) - .setColor(0x20, 0x20, 0x20, 0xFF); - worldr.addVertex(this.right, this.top, 0.0f).setUv(this.right / texScale, (this.top + (int) this.scrollDistance) / texScale) - .setColor(0x20, 0x20, 0x20, 0xFF); - worldr.addVertex(this.left, this.top, 0.0f).setUv(this.left / texScale, (this.top + (int) this.scrollDistance) / texScale) - .setColor(0x20, 0x20, 0x20, 0xFF); - BufferUploader.drawWithShader(worldr.buildOrThrow()); - + // TODO: Rewrite rendering for GuiGraphicsExtractor pipeline final int baseY = this.top + this.border - (int) this.scrollDistance; - this.drawPanel(stack, this.right, baseY, tess, mouseX, mouseY); - - RenderSystem.disableDepthTest(); - - final int extraHeight = (this.getContentHeight() + this.border) - this.height; - if (extraHeight > 0) { - final int barHeight = this.getBarHeight(); - - int barTop = (int) this.scrollDistance * (this.height - barHeight) / extraHeight + this.top; - if (barTop < this.top) { - barTop = this.top; - } - -// RenderSystem.disableTexture(); - RenderSystem.setShader(GameRenderer::getPositionColorShader); - worldr = tess.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - worldr.addVertex(this.barLeft, this.bottom, 0.0f).setColor(0x00, 0x00, 0x00, 0xFF); - worldr.addVertex(this.barLeft + this.barWidth, this.bottom, 0.0f).setColor(0x00, 0x00, 0x00, 0xFF); - worldr.addVertex(this.barLeft + this.barWidth, this.top, 0.0f).setColor(0x00, 0x00, 0x00, 0xFF); - worldr.addVertex(this.barLeft, this.top, 0.0f).setColor(0x00, 0x00, 0x00, 0xFF); - BufferUploader.drawWithShader(worldr.buildOrThrow()); - worldr =tess.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - worldr.addVertex(this.barLeft, barTop + barHeight, 0.0f).setColor(0x80, 0x80, 0x80, 0xFF); - worldr.addVertex(this.barLeft + this.barWidth, barTop + barHeight, 0.0f).setColor(0x80, 0x80, 0x80, 0xFF); - worldr.addVertex(this.barLeft + this.barWidth, barTop, 0.0f).setColor(0x80, 0x80, 0x80, 0xFF); - worldr.addVertex(this.barLeft, barTop, 0.0f).setColor(0x80, 0x80, 0x80, 0xFF); - BufferUploader.drawWithShader(worldr.buildOrThrow()); - worldr = tess.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - worldr.addVertex(this.barLeft, barTop + barHeight - 1, 0.0f).setColor(0xC0, 0xC0, 0xC0, 0xFF); - worldr.addVertex(this.barLeft + this.barWidth - 1, barTop + barHeight - 1, 0.0f).setColor(0xC0, 0xC0, 0xC0, 0xFF); - worldr.addVertex(this.barLeft + this.barWidth - 1, barTop, 0.0f).setColor(0xC0, 0xC0, 0xC0, 0xFF); - worldr.addVertex(this.barLeft, barTop, 0.0f).setColor(0xC0, 0xC0, 0xC0, 0xFF); - BufferUploader.drawWithShader(worldr.buildOrThrow()); - } - -// RenderSystem.enableTexture(); - RenderSystem.disableBlend(); - RenderSystem.disableScissor(); + this.drawPanel(graphics, this.right, baseY, mouseX, mouseY); } @Override diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/list/FilterableList.java b/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/list/FilterableList.java index 3522d2607aa..050272079e4 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/list/FilterableList.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/list/FilterableList.java @@ -24,22 +24,21 @@ */ package org.spongepowered.vanilla.client.gui.widget.list; -import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.AbstractSelectionList; import net.minecraft.client.gui.narration.NarratedElementType; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.narration.NarrationSupplier; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.network.chat.Component; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.vanilla.util.Bounds; import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.function.Consumer; import java.util.function.Supplier; @@ -67,7 +66,7 @@ public Screen getScreen() { } public int getBottom() { - return this.getY() + this.headerHeight; + return this.getY() + this.getHeight(); } public E getCurrentHoveredEntry() { @@ -107,10 +106,10 @@ public P setFilterSupplier(final Supplier> filterSupplier) { this.filterSupplier = filterSupplier; return (P) this; } - - public int getRowHeight() { - return this.itemHeight; - } +// +// public int getRowHeight() { +// return this.itemHeight; +// } @Override public int getRowWidth() { @@ -122,11 +121,6 @@ public int getRowLeft() { return this.getX() + 4; } - @Override - protected int getScrollbarPosition() { - return this.getX() + this.width - 6; - } - @Override public void setSelected(@Nullable final E entry) { if (this.selectConsumer != null) { @@ -136,22 +130,29 @@ public void setSelected(@Nullable final E entry) { super.setSelected(entry); } + @Override - public boolean mouseClicked(final double p_mouseClicked_1_, final double p_mouseClicked_3_, final int p_mouseClicked_5_) { - this.updateScrollingState(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_); - if (!this.isMouseOver(p_mouseClicked_1_, p_mouseClicked_3_)) { + public boolean mouseClicked(final MouseButtonEvent event, final boolean repeatedClick) { + final var mouseX = event.x(); + final var mouseY = event.y(); + final var button = event.button(); + this.updateScrolling(event); + if (!this.isMouseOver(mouseX, mouseY)) { return false; } else { - final E e = this.getEntryAtPosition(p_mouseClicked_1_, p_mouseClicked_3_); + final E e = this.getEntryAtPosition(mouseX, mouseY); if (e != null) { - if (e.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_)) { + if (e.mouseClicked(event, repeatedClick)) { this.setFocused(e); this.setDragging(true); return true; } - } else if (p_mouseClicked_5_ == 0) { - this.clickedHeader((int) (p_mouseClicked_1_ - (double) (this.getX() + this.width / 2 - this.getRowWidth() / 2)), - (int) (p_mouseClicked_3_ - (double) this.getY()) + (int) this.getScrollAmount() - 4); + } else if (button == 0) { + final var newEvent = new MouseButtonEvent( + (mouseX - (double) (this.getX() + this.width / 2 - this.getRowWidth() / 2)), + (mouseY - (double) this.getY()) + (int) this.scrollAmount() - 4, + event.buttonInfo()); + this.onClick(newEvent, repeatedClick); return true; } @@ -159,45 +160,8 @@ public boolean mouseClicked(final double p_mouseClicked_1_, final double p_mouse } } - @Override - protected void renderListItems(final GuiGraphics $$0, final int $$1, final int $$2, final float $$3) { - // Most of this is based on AbstractList::renderList logic - final List filteredList = this.filterSupplier == null ? this.children() : this.filterSupplier.get(); - - if (filteredList.isEmpty()) { - final Font font = this.minecraft.font; - final String noResults = "No results..."; - final int noResultsWidth = font.width(noResults); - - $$0.drawString(font, noResults, (this.width / 2) + this.getX() - (noResultsWidth / 2), this.getY() + 10, ChatFormatting.GRAY.getColor()); - - return; - } - - final int $$4 = this.getRowLeft(); - final int $$5 = this.getRowWidth(); - final int $$6 = this.itemHeight - 4; - final int $$7 = filteredList.size(); - - for (int $$8 = 0; $$8 < $$7; ++$$8) { - final int $$9 = this.getRowTop($$8); - final int $$10 = this.getRowBottom($$8); - if ($$10 >= this.getY() && $$9 <= this.getBottom()) { - this.renderItemFromList(filteredList, $$0, $$1, $$2, $$3, $$8, $$4, $$9, $$5, $$6); - } - } - } - - private void renderItemFromList(final List list, final GuiGraphics $$0, final int $$1, final int $$2, final float $$3, final int $$4, final int $$5, final int $$6, final int $$7, final int $$8) { - final E $$9 = list.get($$4); - $$9.renderBack($$0, $$4, $$6, $$5, $$7, $$8, $$1, $$2, Objects.equals(this.getHovered(), $$9), $$3); - if (this.isSelectedItem($$4)) { - final int $$10 = this.isFocused() ? -1 : -8355712; - this.renderSelection($$0, $$6, $$7, $$8, $$10, -16777216); - } - - $$9.render($$0, $$4, $$6, $$5, $$7, $$8, $$1, $$2, Objects.equals(this.getHovered(), $$9), $$3); - } + // TODO: renderListItems was removed in pre-1 — rendering now handled by extractContent on entries + // See .docs/plugin-gui-rewrite-plan.md @Override protected void updateWidgetNarration(final NarrationElementOutput narrationConsumer) { @@ -232,16 +196,9 @@ public P getParentList() { public abstract Bounds getInteractBounds(); - @SuppressWarnings("unchecked") @Override - public void render(final GuiGraphics stack, final int p_render_1_, final int renderY, final int renderX, final int p_render_4_, - final int p_render_5_, final int mouseX, final int mouseY, final boolean p_render_8_, - final float p_render_9_) { - if (this.getInteractBounds().isInBounds(mouseX, mouseY, renderX, renderY)) { - this.parentList.currentHoveredEntry = (E) this; - } else if (this.parentList.getCurrentHoveredEntry() == this) { - this.parentList.currentHoveredEntry = null; - } + public void extractContent(final GuiGraphicsExtractor graphics, final int mouseX, final int mouseY, final boolean hovered, final float partialTicks) { + // TODO: Implement rendering via GuiGraphicsExtractor (see .docs/plugin-gui-rewrite-plan.md) } } } diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/list/PluginSelectionList.java b/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/list/PluginSelectionList.java index 8790eb4acd5..6c06365b4e2 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/list/PluginSelectionList.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/client/gui/widget/list/PluginSelectionList.java @@ -24,10 +24,11 @@ */ package org.spongepowered.vanilla.client.gui.widget.list; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.narration.NarratedElementType; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.MouseButtonEvent; import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.vanilla.util.Bounds; @@ -50,23 +51,17 @@ public Entry(final PluginSelectionList list, final PluginMetadata metadata) { @Override public Bounds getInteractBounds() { - return new Bounds(0, this.getParentList().getRowWidth(), 0, this.getParentList().getRowHeight()); + return new Bounds(0, this.getParentList().getRowWidth(), 0, this.getParentList().scrollerHeight()); } @Override - public void render(final GuiGraphics stack, final int p_render_1_, final int renderY, final int renderX, final int p_render_4_, - final int p_render_5_, final int mouseX, final int mouseY, final boolean p_render_8_, final float p_render_9_) { - // Draw the name, or ID if name is not present - stack.drawString(this.list.fontRenderer, this.metadata.name().orElse(this.metadata.id()), renderX + 2, renderY + 1, 16777215); - - // Draw the ID if the name is present - if (this.metadata.name().isPresent()) { - stack.drawString(this.list.fontRenderer, this.metadata.id(), renderX + 2, renderY + 12, 8421504); - } + public void extractContent(final GuiGraphicsExtractor graphics, final int mouseX, final int mouseY, final boolean hovered, final float partialTicks) { + // TODO: Implement rendering via GuiGraphicsExtractor (see .docs/plugin-gui-rewrite-plan.md) + // Should draw: plugin name (or ID), and ID below if name is present } @Override - public boolean mouseClicked(final double p_mouseClicked_1_, final double p_mouseClicked_3_, final int p_mouseClicked_5_) { + public boolean mouseClicked(final MouseButtonEvent event, final boolean repeated) { this.getParentList().setSelected(this); return true; } diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/launch/IntegrationTestLaunch.java b/vanilla/src/main/java/org/spongepowered/vanilla/launch/IntegrationTestLaunch.java index ef713e59dc8..104d7b5c9f7 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/launch/IntegrationTestLaunch.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/launch/IntegrationTestLaunch.java @@ -28,7 +28,6 @@ import net.minecraft.server.Bootstrap; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.launch.Launch; -import org.spongepowered.common.launch.Lifecycle; import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; import org.spongepowered.vanilla.applaunch.util.MixinLoggerInjector; @@ -70,11 +69,6 @@ private void performIntegrationTests() { Bootstrap.bootStrap(); Bootstrap.validate(); - // Prepare Sponge - final Lifecycle lifecycle = Launch.instance().lifecycle(); - lifecycle.establishDataProviders(); - lifecycle.callRegisterDataEvent(); - this.logger().info("Performing Mixin audit"); Launch.instance().auditMixins(); } finally { diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/launch/VanillaBootstrap.java b/vanilla/src/main/java/org/spongepowered/vanilla/launch/VanillaBootstrap.java index c37b7391c43..a221bcabff4 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/launch/VanillaBootstrap.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/launch/VanillaBootstrap.java @@ -34,7 +34,6 @@ import org.spongepowered.common.launch.Launch; import org.spongepowered.common.network.channel.SpongeChannelManager; import org.spongepowered.common.network.packet.SpongePacketHandler; -import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; import org.spongepowered.vanilla.launch.plugin.VanillaPluginManager; public final class VanillaBootstrap { @@ -50,7 +49,7 @@ public static void perform(final String engineName, final Runnable engineStart) Launch.instance().setLifecycle(lifecycle); lifecycle.establishFactories(); lifecycle.establishBuilders(); - ((VanillaPluginManager) Launch.instance().pluginManager()).loadPlugins((VanillaPluginPlatform) Launch.instance().pluginPlatform()); + ((VanillaPluginManager) Launch.instance().pluginManager()).loadPlugins(); lifecycle.callConstructEvent(); lifecycle.callRegisterFactoryEvent(); lifecycle.callRegisterBuilderEvent(); diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/launch/VanillaLaunch.java b/vanilla/src/main/java/org/spongepowered/vanilla/launch/VanillaLaunch.java index 34de7cbadd3..219edd712bf 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/launch/VanillaLaunch.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/launch/VanillaLaunch.java @@ -29,16 +29,17 @@ import com.google.inject.Injector; import com.google.inject.Module; import com.google.inject.Stage; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; import org.spongepowered.common.inject.SpongeCommonModule; import org.spongepowered.common.inject.SpongeModule; import org.spongepowered.common.launch.Launch; import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; import org.spongepowered.vanilla.launch.inject.SpongeVanillaModule; import org.spongepowered.vanilla.launch.plugin.VanillaDummyPluginContainer; import org.spongepowered.vanilla.launch.plugin.VanillaPluginManager; -import java.util.Collection; import java.util.List; import java.util.Set; @@ -52,7 +53,7 @@ public abstract class VanillaLaunch extends Launch { protected VanillaLaunch(final VanillaPluginPlatform pluginPlatform, final Stage injectionStage) { super(pluginPlatform); this.injectionStage = injectionStage; - this.pluginManager = new VanillaPluginManager(); + this.pluginManager = new VanillaPluginManager(this); } @Override @@ -73,11 +74,6 @@ public final PluginContainer platformPlugin() { return this.vanillaPlugin; } - @Override - public final VanillaPluginPlatform pluginPlatform() { - return (VanillaPluginPlatform) this.pluginPlatform; - } - @Override public final VanillaPluginManager pluginManager() { return this.pluginManager; @@ -102,9 +98,12 @@ protected final void launchPlatform(final String[] args) { protected abstract void performBootstrap(final String[] args); protected final void createPlatformPlugins() { - this.pluginPlatform().getCandidates().values().stream().flatMap(Collection::stream) - .filter(plugin -> PLATFORM_IDS.contains(plugin.metadata().id())) - .map(plugin -> new VanillaDummyPluginContainer(plugin, this.logger(), this)) - .forEach(this.pluginManager()::addPlugin); + for (final PluginDiscovery.Candidate candidate : this.pluginPlatform().discovery().candidates()) { + for (final PluginMetadata metadata : candidate.metadata()) { + if (VanillaLaunch.PLATFORM_IDS.contains(metadata.id())) { + this.pluginManager().addPlugin(new VanillaDummyPluginContainer(candidate.resource(), metadata)); + } + } + } } } diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/JavaPluginLoader.java b/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/JavaPluginLoader.java deleted file mode 100644 index 432b2e14313..00000000000 --- a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/JavaPluginLoader.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.vanilla.launch.plugin; - -import com.google.inject.Injector; -import org.apache.maven.artifact.versioning.ArtifactVersion; -import org.apache.maven.artifact.versioning.DefaultArtifactVersion; -import org.spongepowered.common.inject.plugin.PluginGuice; -import org.spongepowered.common.launch.Launch; -import org.spongepowered.plugin.Environment; -import org.spongepowered.plugin.InvalidPluginException; -import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.PluginLoader; - -public final class JavaPluginLoader implements PluginLoader { - - private final ArtifactVersion version = new DefaultArtifactVersion("1.0"); - - @Override - public ArtifactVersion version() { - return this.version; - } - - @Override - public VanillaJavaPluginContainer loadPlugin(final Environment environment, final PluginCandidate candidate, final ClassLoader targetClassLoader) - throws InvalidPluginException { - final VanillaJavaPluginContainer container = new VanillaJavaPluginContainer(candidate); - try { - final String mainClass = container.metadata().entrypoint(); - final Class pluginClass = Class.forName(mainClass, true, targetClassLoader); - - final Injector pluginInjector = PluginGuice.create(container, pluginClass, Launch.instance().lifecycle().platformInjector()); - final Object plugin = pluginInjector.getInstance(pluginClass); - container.setInjector(pluginInjector); - container.initializeInstance(plugin); - return container; - } catch (final Exception ex) { - throw new InvalidPluginException("An error occurred creating an instance of plugin '" + container.metadata().id() + "'!", ex); - } - } - -} diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/VanillaDummyPluginContainer.java b/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/VanillaDummyPluginContainer.java index e9de757aa15..a4201db1157 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/VanillaDummyPluginContainer.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/VanillaDummyPluginContainer.java @@ -24,24 +24,20 @@ */ package org.spongepowered.vanilla.launch.plugin; -import com.google.inject.Injector; -import org.apache.logging.log4j.Logger; import org.spongepowered.common.applaunch.plugin.DummyPluginContainer; import org.spongepowered.common.launch.plugin.SpongePluginContainer; -import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.builtin.StandardPluginContainer; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; -import java.util.Optional; +public final class VanillaDummyPluginContainer extends SpongePluginContainer implements DummyPluginContainer { -public final class VanillaDummyPluginContainer extends StandardPluginContainer implements SpongePluginContainer, DummyPluginContainer { - - public VanillaDummyPluginContainer(final PluginCandidate candidate, final Logger logger, final Object instance) { - super(candidate, logger); - this.initializeInstance(instance); + public VanillaDummyPluginContainer(final PluginResource resource, final PluginMetadata metadata) { + super(resource, metadata); } @Override - public Optional injector() { - return Optional.empty(); - } + public void loadMainEntrypoints() {} + + @Override + public void loadSidedEntrypoints(boolean server) {} } diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/VanillaPluginManager.java b/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/VanillaPluginManager.java index 319fc0c42a7..9f01c5a416e 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/VanillaPluginManager.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/VanillaPluginManager.java @@ -27,50 +27,37 @@ import com.google.inject.Singleton; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.apache.logging.log4j.Level; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.common.applaunch.plugin.PluginPlatform; +import org.spongepowered.common.applaunch.plugin.discovery.PluginDiscovery; import org.spongepowered.common.launch.plugin.SpongePluginManager; +import org.spongepowered.common.launch.plugin.loader.PluginCandidate; import org.spongepowered.common.util.PrettyPrinter; -import org.spongepowered.plugin.InvalidPluginException; -import org.spongepowered.plugin.PluginCandidate; import org.spongepowered.plugin.PluginContainer; -import org.spongepowered.plugin.PluginLanguageService; -import org.spongepowered.plugin.PluginLoader; -import org.spongepowered.plugin.PluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.plugin.metadata.model.PluginDependency; -import org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform; import org.spongepowered.vanilla.launch.VanillaLaunch; import org.spongepowered.vanilla.launch.plugin.resolver.DependencyResolver; import org.spongepowered.vanilla.launch.plugin.resolver.ResolutionResult; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; @Singleton public final class VanillaPluginManager implements SpongePluginManager { + private final VanillaLaunch launch; private final Map plugins; private final Map instancesToPlugins; private final List sortedPlugins; - private final Map containerToResource; private boolean ready = false; - public VanillaPluginManager() { + public VanillaPluginManager(final VanillaLaunch launch) { + this.launch = launch; this.plugins = new Object2ObjectOpenHashMap<>(); this.instancesToPlugins = new IdentityHashMap<>(); this.sortedPlugins = new ArrayList<>(); - this.containerToResource = new Object2ObjectOpenHashMap<>(); } + @SuppressWarnings("removal") @Override public Optional fromInstance(final Object instance) { return Optional.ofNullable(this.instancesToPlugins.get(Objects.requireNonNull(instance, "instance"))); @@ -86,33 +73,31 @@ public Collection plugins() { return Collections.unmodifiableCollection(this.sortedPlugins); } - public void loadPlugins(final VanillaPluginPlatform platform) { - final Map pluginLanguageLookup = new HashMap<>(); - final Map> pluginLoaders = new HashMap<>(); - - // Initialise the plugin language loaders. - for (final Map.Entry> candidate : platform.getCandidates().entrySet()) { - final PluginLanguageService languageService = candidate.getKey(); - final String loaderClass = languageService.pluginLoader(); - try { - pluginLoaders.put(languageService, (PluginLoader) Class.forName(loaderClass).getConstructor().newInstance()); - } catch (final InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) { - throw new RuntimeException(e); + public void loadPlugins() { + final PluginPlatform platform = this.launch.pluginPlatform(); + + final List prioritizedCandidates = new LinkedList<>(); + final List candidates = new LinkedList<>(); + for (final PluginDiscovery.Candidate resourceCandidate : platform.discovery().candidates()) { + for (final PluginMetadata metadata : resourceCandidate.metadata()) { + final List target = this.plugins.containsKey(metadata.id()) ? prioritizedCandidates : candidates; + try { + target.add(this.launch.pluginFactory().create(resourceCandidate.resource(), metadata)); + } catch (final Exception e) { + platform.logger().error("Cannot create plugin {}.", metadata.id(), e); + } } - candidate.getValue().forEach(x -> pluginLanguageLookup.put(x, languageService)); } // Priority to platform plugins that will already exist here -- meaning the resolver will act upon them first // and if someone decides to give a plugin an ID that is the same as a platform plugin, the resolver will effectively // reject it. - final Set resources = new LinkedHashSet<>(); - pluginLanguageLookup.keySet().stream().filter(x -> this.plugins.containsKey(x.metadata().id())).forEach(resources::add); - resources.addAll(pluginLanguageLookup.keySet()); + candidates.addAll(0, prioritizedCandidates); - final ResolutionResult resolutionResult = DependencyResolver.resolveAndSortCandidates(resources, platform.logger()); + final ResolutionResult resolutionResult = DependencyResolver.resolveAndSortCandidates(candidates, platform.logger()); final Map failedInstances = new HashMap<>(); final Map consequentialFailedInstances = new HashMap<>(); - final ClassLoader launchClassloader = VanillaLaunch.instance().getClass().getClassLoader(); + for (final PluginCandidate candidate : resolutionResult.sortedSuccesses()) { final String id = candidate.metadata().id(); if (id.indexOf('-') >= 0) { @@ -149,13 +134,10 @@ public void loadPlugins(final VanillaPluginPlatform platform) { // If a dependency failed to load, then we should bail on required dependencies too. // This should work fine, we're sorted so all deps should be in place at this stage. if (this.stillValid(candidate, consequentialFailedInstances)) { - final PluginLanguageService languageService = pluginLanguageLookup.get(candidate); - final PluginLoader pluginLoader = pluginLoaders.get(languageService); try { - final PluginContainer container = pluginLoader.loadPlugin(platform.getEnvironment(), candidate, launchClassloader); + final PluginContainer container = candidate.load(); this.addPlugin(container); - this.containerToResource.put(container, candidate.resource()); - } catch (final InvalidPluginException e) { + } catch (final Exception e) { failedInstances.put(candidate, "Failed to construct: see stacktrace(s) above this message for details."); platform.logger().error("Failed to construct plugin {}", id, e); } @@ -168,6 +150,7 @@ public void loadPlugins(final VanillaPluginPlatform platform) { this.ready = true; } + @SuppressWarnings("removal") public void addPlugin(final PluginContainer plugin) { this.plugins.put(plugin.metadata().id(), Objects.requireNonNull(plugin, "plugin")); this.sortedPlugins.add(plugin); @@ -177,11 +160,6 @@ public void addPlugin(final PluginContainer plugin) { } } - @Nullable - public PluginResource resource(final PluginContainer container) { - return this.containerToResource.get(container); - } - private boolean stillValid(final PluginCandidate candidate, final Map consequential) { final Optional failedId = candidate.metadata().dependencies().stream().filter(d -> !this.isDependencyStillValid(d)).findFirst(); diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/resolver/DependencyResolver.java b/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/resolver/DependencyResolver.java index 3d53223bac9..f88d646cf7c 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/resolver/DependencyResolver.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/resolver/DependencyResolver.java @@ -25,11 +25,9 @@ package org.spongepowered.vanilla.launch.plugin.resolver; import org.apache.logging.log4j.Logger; -import org.apache.maven.artifact.versioning.ArtifactVersion; -import org.apache.maven.artifact.versioning.VersionRange; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.util.Tuple; -import org.spongepowered.plugin.PluginCandidate; +import org.spongepowered.common.applaunch.plugin.VersionChecker; +import org.spongepowered.common.launch.plugin.loader.PluginCandidate; import org.spongepowered.plugin.metadata.model.PluginDependency; import java.util.ArrayList; @@ -40,7 +38,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -83,7 +80,7 @@ public static ResolutionResult resolveAndSortCandidates(final Collection nodes, final ResolutionResult resolutionResult) { for (final Node node : nodes) { if (!node.checked) { diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/resolver/ResolutionResult.java b/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/resolver/ResolutionResult.java index fb1124b893b..1ce1652bfcd 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/resolver/ResolutionResult.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/launch/plugin/resolver/ResolutionResult.java @@ -27,8 +27,8 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; import org.spongepowered.api.util.Tuple; +import org.spongepowered.common.launch.plugin.loader.PluginCandidate; import org.spongepowered.common.util.PrettyPrinter; -import org.spongepowered.plugin.PluginCandidate; import java.util.Collection; import java.util.HashMap; diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginPackResources.java b/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginPackResources.java index 067a29faa37..c8d926ff780 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginPackResources.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginPackResources.java @@ -26,39 +26,47 @@ import com.mojang.logging.LogUtils; import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.packs.AbstractPackResources; import net.minecraft.server.packs.PackLocationInfo; 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.PackFormat; import net.minecraft.server.packs.metadata.pack.PackMetadataSection; import net.minecraft.server.packs.resources.IoSupplier; +import net.minecraft.util.InclusiveRange; import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.vanilla.applaunch.plugin.discovery.SecureJarPluginResource; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.Collections; +import java.util.Locale; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; public final class PluginPackResources extends AbstractPackResources { private static final Logger LOGGER = LogUtils.getLogger(); - private final PluginContainer container; + private final PluginResource resource; private final PackMetadataSection metadata; private final @Nullable Path pluginRoot; - public PluginPackResources(final PackLocationInfo info, final PluginContainer container, final @Nullable Path pluginRoot) { + public PluginPackResources(final PackLocationInfo info, final PluginResource resource) { super(info); - this.container = container; - this.metadata = new PackMetadataSection(Component.literal("Plugin Resources"), 6, Optional.empty()); - this.pluginRoot = pluginRoot; + this.resource = resource; + this.metadata = new PackMetadataSection(Component.literal("Plugin Resources"), new InclusiveRange<>(PackFormat.of(6))); + // TODO: provide hook in the resource to return the file system for all resource types? + this.pluginRoot = resource instanceof SecureJarPluginResource jarResource ? jarResource.jar().getRootPath() : null; } @Override @@ -68,7 +76,7 @@ public IoSupplier getRootResource(final String... var1) { } private IoSupplier getResource(final String rawPath) { - final Optional uri = this.container.locateResource(rawPath); + final Optional uri = this.resource.locate(rawPath); if (uri.isEmpty()) { return null; } @@ -76,7 +84,7 @@ private IoSupplier getResource(final String rawPath) { } @Override - public IoSupplier getResource(final PackType type, final ResourceLocation loc) { + public IoSupplier getResource(final PackType type, final Identifier loc) { return this.getResource(String.format(Locale.ROOT, "%s/%s/%s", type.getDirectory(), loc.getNamespace(), loc.getPath())); } @@ -99,9 +107,9 @@ public void listResources(final PackType type, final String namespace, final Str } @Nullable - private ResourceLocation convertResourcePath(final String namespace, final Path resourcePath) { + private Identifier convertResourcePath(final String namespace, final Path resourcePath) { final String path = resourcePath.toString(); - final ResourceLocation location = ResourceLocation.tryBuild(namespace, path); + final Identifier location = Identifier.tryBuild(namespace, path); if (location == null) LOGGER.warn("Invalid path in plugin pack: {}:{}, ignoring", namespace, path); return location; @@ -109,8 +117,8 @@ private ResourceLocation convertResourcePath(final String namespace, final Path @Nullable @Override - public T getMetadataSection(final MetadataSectionSerializer deserializer) throws IOException { - if (deserializer.getMetadataSectionName().equals("pack")) { + public T getMetadataSection(final MetadataSectionType deserializer) throws IOException { + if (deserializer.name().equals("pack")) { return (T) this.metadata; } return null; diff --git a/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginRepositorySource.java b/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginRepositorySource.java index 7d2b6972662..40be5e1f2d6 100644 --- a/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginRepositorySource.java +++ b/vanilla/src/main/java/org/spongepowered/vanilla/server/packs/PluginRepositorySource.java @@ -33,15 +33,11 @@ import net.minecraft.server.packs.repository.PackRepository; import net.minecraft.server.packs.repository.PackSource; import net.minecraft.server.packs.repository.RepositorySource; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.common.launch.Launch; import org.spongepowered.plugin.PluginContainer; -import org.spongepowered.plugin.PluginResource; -import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; import org.spongepowered.vanilla.bridge.server.packs.repository.PackRepositoryBridge_Vanilla; import org.spongepowered.vanilla.launch.plugin.VanillaPluginManager; -import java.nio.file.Path; import java.util.Optional; import java.util.function.Consumer; @@ -63,15 +59,8 @@ public void loadPacks(final Consumer callback) { // The pack ID is prepended with "plugin-", as this will be the namespace we have to use a valid // character as a separator final String id = "plugin-" + pluginContainer.metadata().id(); - final PluginResource resource = pluginManager.resource(pluginContainer); - // TODO: provide hook in the resource to return the file system for all resource types? - @Nullable Path pluginRoot = null; - if (resource instanceof JVMPluginResource jvmResource) { - pluginRoot = jvmResource.resourcesRoot(); - } - final PackLocationInfo info = new PackLocationInfo(id, Component.literal(id), PackSource.DEFAULT, Optional.empty()); - final PluginPackResources packResources = new PluginPackResources(info, pluginContainer, pluginRoot); + final PluginPackResources packResources = new PluginPackResources(info, pluginContainer.resource()); final Pack.ResourcesSupplier packSupplier = new Pack.ResourcesSupplier() { @Override diff --git a/vanilla/src/main/resource-templates/META-INF/sponge_plugins.json b/vanilla/src/main/resource-templates/META-INF/sponge_plugins.json index 5a9c035285f..9f8ee4b862a 100644 --- a/vanilla/src/main/resource-templates/META-INF/sponge_plugins.json +++ b/vanilla/src/main/resource-templates/META-INF/sponge_plugins.json @@ -1,56 +1,45 @@ { - "loader": { - "name": "java_plain", - "version": "1.0" + "global": { + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "MIT", + "links": { + "homepage": "https://www.spongepowered.org", + "source": "https://www.spongepowered.org/source", + "issues": "https://www.spongepowered.org/issues" + }, + "contributors": [ + { + "name": "SpongePowered", + "description": "Lead Developer" + } + ] }, - "license": "MIT", "plugins": [ { "id": "spongevanilla", "name": "SpongeVanilla", "version": "{{ version }}", - "entrypoint": "org.spongepowered.vanilla.SpongeVanilla", - "description": "Vanilla Minecraft with Sponge", - "links": { - "homepage": "https://www.spongepowered.org", - "source": "https://www.spongepowered.org/source", - "issues": "https://www.spongepowered.org/issues" - }, - "contributors": [ - { - "name": "SpongePowered", - "description": "Lead Developer" - } - ], + "description": "{{ description }}", "dependencies": [ + { + "id": "minecraft", + "version": "{{ minecraftVersion }}" + }, { "id": "sponge", - "version": "{{ minecraftVersion }}-{{ apiVersion }}" + "version": "{{ version }}" } ] }, { "id": "sponge", "name": "Sponge", - "version": "{{ minecraftVersion }}-{{ apiVersion }}", - "entrypoint": "org.spongepowered.common.SpongeCommon", + "version": "{{ version }}", "description": "The common Sponge implementation", - "links": { - "homepage": "https://www.spongepowered.org", - "source": "https://www.spongepowered.org/source", - "issues": "https://www.spongepowered.org/issues" - }, - "contributors": [ - { - "name": "SpongePowered", - "description": "Lead Developer" - } - ], "dependencies": [ - { - "id": "minecraft", - "version": "{{ minecraftVersion }}" - }, { "id": "spongeapi", "version": "{{ apiVersion }}" @@ -61,19 +50,7 @@ "id": "spongeapi", "name": "SpongeAPI", "version": "{{ apiVersion }}", - "entrypoint": "org.spongepowered.api.Sponge", - "description": "The Minecraft API specification", - "links": { - "homepage": "https://www.spongepowered.org", - "source": "https://www.spongepowered.org/source", - "issues": "https://www.spongepowered.org/issues" - }, - "contributors": [ - { - "name": "SpongePowered", - "description": "Lead Developer" - } - ] + "description": "The Minecraft API specification" } ] } \ No newline at end of file diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/MinecraftMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/MinecraftMixin_Vanilla.java index 55ef4f6d204..67f9524d10d 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/MinecraftMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/MinecraftMixin_Vanilla.java @@ -26,7 +26,6 @@ import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -40,8 +39,6 @@ @Mixin(Minecraft.class) public abstract class MinecraftMixin_Vanilla implements MinecraftBridge, VanillaClient { - @Shadow public abstract void resizeDisplay(); - @Override public ClientType bridge$getClientType() { return ClientType.SPONGE_VANILLA; @@ -50,9 +47,6 @@ public abstract class MinecraftMixin_Vanilla implements MinecraftBridge, Vanilla @Inject(method = "run", at = @At("HEAD")) private void vanilla$establishRegistriesAndStartingEngine(CallbackInfo ci) { final Lifecycle lifecycle = Launch.instance().lifecycle(); - lifecycle.establishDataProviders(); - lifecycle.callRegisterDataEvent(); - lifecycle.establishClientRegistries(this); lifecycle.callStartingEngineEvent(this); } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/gui/screens/TitleScreenMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/gui/screens/TitleScreenMixin_Vanilla.java index 22dcc76be2b..9f51567f48d 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/gui/screens/TitleScreenMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/gui/screens/TitleScreenMixin_Vanilla.java @@ -57,7 +57,7 @@ protected TitleScreenMixin_Vanilla(final Component titleIn) { // Plugins Button this.addRenderableWidget(Button.builder(Component.literal("Plugins"), - b -> this.minecraft.setScreen(new PluginScreen(this))) + b -> this.minecraft.setScreenAndShow(new PluginScreen(this))) .pos(this.width / 2 - 100, realmsButton.map(b -> b.getY()).orElse(0)) .size(98, 20) .build()); diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/server/IntegratedServerMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/server/IntegratedServerMixin_Vanilla.java index 9d4f91c42b2..b34dcebf928 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/server/IntegratedServerMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/client/server/IntegratedServerMixin_Vanilla.java @@ -47,11 +47,8 @@ public abstract class IntegratedServerMixin_Vanilla extends MinecraftServerMixin ((MinecraftBridge) Minecraft.getInstance()).bridge$setTemporaryIntegratedServer((IntegratedServer) (Object) this); } final Lifecycle lifecycle = Launch.instance().lifecycle(); - lifecycle.establishServerServices(); - lifecycle.establishServerFeatures(); - lifecycle.establishServerRegistries(this); lifecycle.callStartingEngineEvent(this); } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/commands/CommandsMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/commands/CommandsMixin_Vanilla.java index bae8269ee2c..fd1c9f7148c 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/commands/CommandsMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/commands/CommandsMixin_Vanilla.java @@ -27,14 +27,12 @@ import com.mojang.brigadier.tree.CommandNode; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.server.level.ServerPlayer; import org.spongepowered.api.command.CommandCause; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; @@ -42,51 +40,36 @@ import org.spongepowered.common.bridge.commands.CommandSourceStackBridge; import org.spongepowered.common.bridge.commands.CommandsBridge; import org.spongepowered.common.command.manager.SpongeCommandManager; +import org.spongepowered.common.command.resolver.SpongeSuggestionTreeResolver; import org.spongepowered.common.event.tracking.PhaseTracker; import java.util.IdentityHashMap; -import java.util.List; import java.util.Map; -import java.util.WeakHashMap; @Mixin(Commands.class) public abstract class CommandsMixin_Vanilla implements CommandsBridge { - // @formatter:off - @Shadow private void shadow$fillUsableCommands(final CommandNode rootCommandSource, - final CommandNode rootSuggestion, - final CommandSourceStack source, - final Map, CommandNode> commandNodeToSuggestionNode) { - throw new AssertionError("This shouldn't be callable"); - } - // @formatter:on - - private CauseStackManager.StackFrame impl$initFrame = null; - private WeakHashMap, List>>> impl$playerNodeCache; private SpongeCommandManager impl$commandManager; - @Redirect(method = "sendCommands", at = @At(value = "INVOKE", target = "Lnet/minecraft/commands/Commands;fillUsableCommands(Lcom/mojang/brigadier/tree/CommandNode;Lcom/mojang/brigadier/tree/CommandNode;Lnet/minecraft/commands/CommandSourceStack;Ljava/util/Map;)V")) - private void impl$addNonBrigSuggestions( - final Commands commands, - final CommandNode p_197052_1_, - final CommandNode p_197052_2_, - final CommandSourceStack p_197052_3_, - final Map, CommandNode> p_197052_4_, + @Redirect(method = "sendCommands", at = @At(value = "INVOKE", target = "Lnet/minecraft/commands/Commands;fillUsableCommands(Lcom/mojang/brigadier/tree/CommandNode;Lcom/mojang/brigadier/tree/CommandNode;Ljava/lang/Object;Ljava/util/Map;)V")) + private void impl$addNonBrigSuggestions( + final CommandNode rootCommandNode, + final CommandNode rootSuggestion, + final S source, + final Map, CommandNode> commandToSuggestion, final ServerPlayer playerEntity) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { frame.pushCause(playerEntity); frame.addContext(EventContextKeys.SUBJECT, (Subject) playerEntity); - final CommandCause sourceToUse = ((CommandSourceStackBridge) p_197052_3_).bridge$withCurrentCause(); - try { - this.impl$playerNodeCache.put(playerEntity, new IdentityHashMap<>()); - // We use this because the redirects should be a 1:1 mapping (which is what this map is for). - final IdentityHashMap, CommandNode> idMap = new IdentityHashMap<>(p_197052_4_); - this.shadow$fillUsableCommands(p_197052_1_, p_197052_2_, (CommandSourceStack) sourceToUse, idMap); - } finally { - this.impl$playerNodeCache.remove(playerEntity); - } - for (final CommandNode node : this.impl$commandManager.getNonBrigadierSuggestions(sourceToUse)) { - p_197052_2_.addChild(node); + final CommandCause sourceToUse = ((CommandSourceStackBridge) source).bridge$withCurrentCause(); + + // We use this because the redirects should be a 1:1 mapping (which is what this map is for). + final IdentityHashMap, CommandNode> idMap = new IdentityHashMap(commandToSuggestion); + new SpongeSuggestionTreeResolver((CommandSourceStack) sourceToUse, idMap, new IdentityHashMap<>(), this.impl$commandManager) + .fillSuggestions((CommandNode) rootCommandNode, (CommandNode) rootSuggestion); + + for (final CommandNode node : this.impl$commandManager.getNonBrigadierSuggestions(sourceToUse)) { + rootSuggestion.addChild((CommandNode) node); } } } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/commands/arguments/selector/EntitySelectorMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/commands/arguments/selector/EntitySelectorMixin_Vanilla.java index 1228d58f96b..3f693e13c4d 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/commands/arguments/selector/EntitySelectorMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/commands/arguments/selector/EntitySelectorMixin_Vanilla.java @@ -24,22 +24,18 @@ */ package org.spongepowered.vanilla.mixin.core.commands.arguments.selector; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.arguments.selector.EntitySelector; -import net.minecraft.commands.arguments.selector.EntitySelectorParser; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; @Mixin(EntitySelector.class) public abstract class EntitySelectorMixin_Vanilla { - @WrapOperation(method = "checkPermissions", at = @At(value = "INVOKE", target = "Lnet/minecraft/commands/CommandSourceStack;hasPermission(I)Z")) - private boolean vanilla$onCheckSelectorPermission(final CommandSourceStack instance, final int $$0, final Operation original) { - if (EntitySelectorParser.allowSelectors(instance)) { - return true; - } - return original.call(instance, $$0); - } + // TODO - 25w20a changes +// @WrapOperation(method = "checkPermissions", at = @At(value = "INVOKE", target = "Lnet/minecraft/commands/CommandSourceStack;hasPermission(I)Z")) +// private boolean vanilla$onCheckSelectorPermission(final CommandSourceStack instance, final int $$0, final Operation original) { +// if (EntitySelectorParser.allowSelectors(instance)) { +// return true; +// } +// return original.call(instance, $$0); +// } } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/network/ConnectionMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/network/ConnectionMixin_Vanilla.java index f144a638e63..65aaaba8dd8 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/network/ConnectionMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/network/ConnectionMixin_Vanilla.java @@ -24,22 +24,38 @@ */ package org.spongepowered.vanilla.mixin.core.network; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; import net.minecraft.network.Connection; -import net.minecraft.network.PacketSendListener; +import net.minecraft.network.protocol.Packet; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.network.channel.PacketSender; @Mixin(Connection.class) public class ConnectionMixin_Vanilla { - @Inject(method = "lambda$doSendPacket$13", at = @At(value = "INVOKE", target = "Lio/netty/util/concurrent/Future;isSuccess()Z")) - public void impl$onPacketSent(final PacketSendListener listener, final Future future, final CallbackInfo ci) { - if (listener instanceof final PacketSender.SpongePacketSendListener spongeListener) { - spongeListener.accept(future.cause()); + @WrapOperation(method = "doSendPacket", at = @At( + value = "INVOKE", + target = "Lio/netty/channel/ChannelFuture;addListener(Lio/netty/util/concurrent/GenericFutureListener;)Lio/netty/channel/ChannelFuture;", + remap = false + )) + public ChannelFuture impl$onPacketSent( + final ChannelFuture instance, final GenericFutureListener> genericFutureListener, + final Operation original, final Packet packet, + final ChannelFutureListener listener, final boolean abool + ) { + if (listener instanceof PacketSender.SpongePacketSendListener spsl) { + original.call(instance, (ChannelFutureListener) (future) -> { + if (!future.isSuccess()) { + spsl.accept(future.cause()); + } + }); } + return original.call(instance, listener); } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/registries/BuiltInRegistriesMixin.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/registries/BuiltInRegistriesMixin_Vanilla.java similarity index 66% rename from src/mixins/java/org/spongepowered/common/mixin/core/registries/BuiltInRegistriesMixin.java rename to vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/registries/BuiltInRegistriesMixin_Vanilla.java index 01b7d782789..e8d8655da46 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/registries/BuiltInRegistriesMixin.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/registries/BuiltInRegistriesMixin_Vanilla.java @@ -22,40 +22,30 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.registries; - +package org.spongepowered.vanilla.mixin.core.registries; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.launch.Launch; - -import java.util.Map; -import java.util.function.Supplier; +import org.spongepowered.common.launch.Lifecycle; @Mixin(BuiltInRegistries.class) -public abstract class BuiltInRegistriesMixin { - - // We hook in here if we extend existing vanilla BuiltInRegistries with our own - // This methods should then be called during bootstrap - - @Shadow @Final private static Map> LOADERS; - - @Inject(method = "bootStrap", at = @At(value = "HEAD")) - private static void impl$beforeCreateContents(CallbackInfo ci) - { - Launch.instance().lifecycle().establishEarlyGlobalRegistries(); - } +public abstract class BuiltInRegistriesMixin_Vanilla { @Inject(method = "bootStrap", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/registries/BuiltInRegistries;freeze()V")) - private static void impl$beforeFreeze(CallbackInfo ci) - { - Launch.instance().lifecycle().finalizeEarlyGlobalRegistries(); + private static void impl$beforeFreeze(final CallbackInfo ci) { + final Lifecycle lifecycle = Launch.instance().lifecycle(); + lifecycle.establishDataProviders(); + lifecycle.callRegisterDataEvent(); + lifecycle.establishEarlyGlobalRegistries(); + lifecycle.establishGlobalRegistries(); } + @Inject(method = "freeze", at = @At("TAIL")) + private static void impl$afterFreeze(final CallbackInfo ci) { + Launch.instance().lifecycle().endEstablishGlobalRegistries(); + } } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/resources/RegistryDataLoaderMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/resources/RegistryDataLoaderMixin_Vanilla.java new file mode 100644 index 00000000000..e8b9aef7d52 --- /dev/null +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/resources/RegistryDataLoaderMixin_Vanilla.java @@ -0,0 +1,121 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.mixin.core.resources; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.core.RegistryAccess; +import net.minecraft.resources.RegistryDataLoader; +import net.minecraft.resources.RegistryLoadTask; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.DependencySorter; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryRoots; +import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.accessor.resources.RegistryDataLoader_ResourceManagerRegistryLoadTaskAccessor; +import org.spongepowered.common.accessor.resources.RegistryLoadTaskAccessor; +import org.spongepowered.common.bridge.core.WritableRegistryBridge; +import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.launch.Lifecycle; +import org.spongepowered.common.registry.SpongeRegistryDependencyEntry; +import org.spongepowered.common.registry.SpongeRegistryHolder; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Mixin(RegistryDataLoader.class) +public class RegistryDataLoaderMixin_Vanilla { + + @SuppressWarnings({"rawtypes", "unchecked"}) + @WrapOperation(method = "lambda$load$2", + at = @At(value = "INVOKE", target = "Ljava/util/List;stream()Ljava/util/stream/Stream;", ordinal = 0)) + private static Stream impl$onLoad( + List> instance, Operation original + ) { + // TODO: Remote layer + final DependencySorter, SpongeRegistryDependencyEntry>> dependencies = new DependencySorter<>(); + final Lifecycle lifecycle = Launch.instance().lifecycle(); + final var collect = instance.stream() + .collect(Collectors.groupingBy(l -> { + if (l instanceof RegistryDataLoader_ResourceManagerRegistryLoadTaskAccessor accessor) { + return accessor.accessor$getResourceManager(); + } + return ResourceManager.Empty.INSTANCE; + }, Collectors.toSet())); + collect + .forEach((k, v) -> { + final RegistryHolder registryHolder; + if (k == ResourceManager.Empty.INSTANCE) { + // on the client side, we don't have a real resource manager + registryHolder = SpongeCommon.game(); + } else { + registryHolder = (RegistryHolder) k; + } + // Build a combined registry list: new batch registries replace any existing ones + // with the same key (e.g., on re-join, the second load produces fresh registries + // for the same keys that were already in the root from the first load). + final var batchRegistries = v.stream() + .map(RegistryLoadTaskAccessor.class::cast) + .map(RegistryLoadTaskAccessor::accessor$registry) + .toList(); + final var batchKeys = batchRegistries.stream() + .map(net.minecraft.core.Registry::key) + .collect(java.util.stream.Collectors.toSet()); + ((SpongeRegistryHolder) registryHolder).setRootMinecraftRegistry(new RegistryAccess.ImmutableRegistryAccess( + (List) Stream.concat( + registryHolder.streamRegistries(RegistryRoots.MINECRAFT) + .filter(r -> !batchKeys.contains(((net.minecraft.core.Registry) r).key())), + batchRegistries.stream() + ).toList())); + + // Only fire plugin registry events on the server side (ResourceManager-backed loads). + // Client-side network loads only receive SYNCHRONIZED_REGISTRIES and lack server-only + // registries (placed_feature, configured_feature, etc.) that plugins may declare as + // dependencies, causing freeze validation to fail. + if (k != ResourceManager.Empty.INSTANCE) { + lifecycle.processServerRegistries(registryHolder, v.stream() + .map(RegistryLoadTaskAccessor.class::cast) + .filter(l -> !RegistryDataLoader.DIMENSION_REGISTRIES.contains(l.accessor$data())) // NOTE: DIMENSION_REGISTRIES are special! + .map(l -> (Registry) l.accessor$registry())); + } + + v + .stream().map(RegistryLoadTaskAccessor.class::cast) + .forEach(l -> dependencies.addEntry(((Registry) l.accessor$registry()).type(), + new SpongeRegistryDependencyEntry<>(l, ((WritableRegistryBridge) l.accessor$registry()).bridge$pendingDependencies().toList()))); + }); + + List> loaders = new ArrayList<>(collect.size()); + dependencies.orderByDependencies(($, v) -> loaders.add(v.cookie())); + + return original.call(loaders); + } +} diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/MinecraftServerMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/MinecraftServerMixin_Vanilla.java index 5d985daca65..88f71ae10ca 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/MinecraftServerMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/MinecraftServerMixin_Vanilla.java @@ -24,15 +24,24 @@ */ package org.spongepowered.vanilla.mixin.core.server; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.packs.resources.MultiPackResourceManager; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Coerce; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.accessor.server.MinecraftServer_ReloadableResourcesAccessor; +import org.spongepowered.common.bridge.server.MinecraftServerBridge; import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.registry.SpongeRegistryHolder; import org.spongepowered.vanilla.VanillaServer; +import java.util.Collection; + @Mixin(MinecraftServer.class) public abstract class MinecraftServerMixin_Vanilla implements VanillaServer { @@ -49,4 +58,15 @@ public String getServerModName() { private void vanilla$callStoppingEngineEvent(final CallbackInfo ci) { Launch.instance().lifecycle().callStoppingEngineEvent(this); } + + @ModifyExpressionValue(method = "lambda$reloadResources$1", at = @At(value = "NEW", target = "Lnet/minecraft/server/packs/resources/MultiPackResourceManager;")) + private MultiPackResourceManager impl$onReloadResources(final MultiPackResourceManager original) { + ((MinecraftServerBridge) this).bridge$reloadServerRegistries((RegistryHolder) original); + return original; + } + + @Inject(method = "lambda$reloadResources$4", at = @At("TAIL")) + public void impl$onReloadedResources(final Collection $$0x, final @Coerce MinecraftServer_ReloadableResourcesAccessor $$1x, final CallbackInfo ci) { + ((MinecraftServerBridge) this).bridge$reloadedServerRegistries(((SpongeRegistryHolder) $$1x.accessor$resourceManager()).registryHolder()); + } } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/ReloadableServerResourcesMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/ReloadableServerResourcesMixin_Vanilla.java new file mode 100644 index 00000000000..3643202d34a --- /dev/null +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/ReloadableServerResourcesMixin_Vanilla.java @@ -0,0 +1,66 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.mixin.core.server; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.commands.Commands; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.LayeredRegistryAccess; +import net.minecraft.server.ReloadableServerResources; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.permissions.PermissionSet; +import net.minecraft.world.flag.FeatureFlagSet; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.common.bridge.server.packs.resources.ResourceManagerBridge; +import org.spongepowered.common.command.brigadier.dispatcher.DelegatingCommandDispatcher; + +import java.util.List; + +@Mixin(ReloadableServerResources.class) +public abstract class ReloadableServerResourcesMixin_Vanilla { + + @WrapOperation(method = "lambda$loadResources$2", at = @At(value = "NEW", target = "(Lnet/minecraft/core/LayeredRegistryAccess;Lnet/minecraft/core/HolderLookup$Provider;Lnet/minecraft/world/flag/FeatureFlagSet;Lnet/minecraft/commands/Commands$CommandSelection;Ljava/util/List;Lnet/minecraft/server/permissions/PermissionSet;Ljava/util/List;)Lnet/minecraft/server/ReloadableServerResources;")) + private static ReloadableServerResources impl$onCreateResources( + final LayeredRegistryAccess fullLayers, + final HolderLookup.Provider loadingContext, + final FeatureFlagSet enabledFeatures, + final Commands.CommandSelection commandSelection, + final List postponedTags, + final PermissionSet functionCompilationPermissions, + final List newComponents, + final Operation original, + final @Local(argsOnly = true) ResourceManager resourceManager + ) { + final ReloadableServerResources instance = original.call(fullLayers, loadingContext, enabledFeatures, commandSelection, postponedTags, functionCompilationPermissions, newComponents); + if (instance.getCommands().getDispatcher() instanceof + final DelegatingCommandDispatcher delegatingCommandDispatcher) { + delegatingCommandDispatcher.permissionService(((ResourceManagerBridge) resourceManager).bridge$services().permissionService()); + } + return instance; + } +} diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/dedicated/DedicatedServerMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/dedicated/DedicatedServerMixin_Vanilla.java index 71e26883ce0..adcab4a3f67 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/dedicated/DedicatedServerMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/dedicated/DedicatedServerMixin_Vanilla.java @@ -43,11 +43,8 @@ public abstract class DedicatedServerMixin_Vanilla extends MinecraftServerMixin_ ConfigHandle.setSaveSuppressed(false); final Lifecycle lifecycle = Launch.instance().lifecycle(); - lifecycle.establishServerServices(); - lifecycle.establishServerFeatures(); - lifecycle.establishServerRegistries(this); lifecycle.callStartingEngineEvent(this); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/levelgen/structure/LegacyStructureDataHandlerMixin.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/level/ServerLevelMixin_Vanilla.java similarity index 51% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/level/levelgen/structure/LegacyStructureDataHandlerMixin.java rename to vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/level/ServerLevelMixin_Vanilla.java index c59bb5b2546..f62ecbbb4e5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/levelgen/structure/LegacyStructureDataHandlerMixin.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/level/ServerLevelMixin_Vanilla.java @@ -22,28 +22,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.level.levelgen.structure; +package org.spongepowered.vanilla.mixin.core.server.level; -import net.minecraft.core.registries.Registries; +import net.minecraft.core.Holder; +import net.minecraft.core.RegistryAccess; +import net.minecraft.network.protocol.Packet; import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.players.PlayerList; import net.minecraft.world.level.Level; -import net.minecraft.world.level.levelgen.structure.LegacyStructureDataHandler; -import net.minecraft.world.level.storage.DimensionDataStorage; -import org.checkerframework.checker.nullness.qual.Nullable; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.storage.WritableLevelData; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; -import org.spongepowered.common.bridge.world.level.storage.DimensionDataStorageBridge; +import org.spongepowered.asm.mixin.injection.Redirect; -import java.util.Optional; +@Mixin(ServerLevel.class) +public abstract class ServerLevelMixin_Vanilla extends Level { -@Mixin(LegacyStructureDataHandler.class) -public abstract class LegacyStructureDataHandlerMixin { + protected ServerLevelMixin_Vanilla(WritableLevelData levelData, ResourceKey dimension, RegistryAccess registryAccess, Holder dimensionTypeRegistration, boolean isClientSide, boolean isDebug, long biomeZoomSeed, int maxChainedNeighborUpdates) { + super(levelData, dimension, registryAccess, dimensionTypeRegistration, isClientSide, isDebug, biomeZoomSeed, maxChainedNeighborUpdates); + } - @ModifyVariable(method = "getLegacyStructureHandler", at = @At("HEAD"), argsOnly = true) - private static ResourceKey impl$fixupLevelDimension(final ResourceKey dimension, final ResourceKey $$0, final @Nullable DimensionDataStorage $$1) { - return Optional.ofNullable(((DimensionDataStorageBridge) $$1).bridge$dimensionKey()) - .map(k -> ResourceKey.create(Registries.DIMENSION, k)) - .orElse(dimension); + @Redirect( + method = "advanceWeatherCycle", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcastAll(Lnet/minecraft/network/protocol/Packet;)V") + ) + private void vanilla$broadcastAllCurrentDimensionOnly(final PlayerList instance, final Packet packet) { + instance.broadcastAll(packet, this.dimension()); } } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/level/ServerPlayerMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/level/ServerPlayerMixin_Vanilla.java index 0718cf4e114..a99e11732f0 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/level/ServerPlayerMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/level/ServerPlayerMixin_Vanilla.java @@ -24,8 +24,10 @@ */ package org.spongepowered.vanilla.mixin.core.server.level; +import net.minecraft.server.level.ServerLevel; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.bridge.server.level.ServerPlayerBridge; import org.spongepowered.common.event.tracking.PhaseContext; @@ -38,10 +40,14 @@ @Mixin(net.minecraft.server.level.ServerPlayer.class) public abstract class ServerPlayerMixin_Vanilla extends LivingEntityMixin_Vanilla implements ServerPlayerBridge { + // @formatter:off + @Shadow public abstract ServerLevel shadow$level(); + // @formatter:on + // override from LivingEntityMixin_Vanilla @Override protected void vanilla$onElytraUse(final CallbackInfo ci) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(this.shadow$level()).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); final net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer) (Object) this; try (final EffectTransactor ignored = transactor.logPlayerInventoryChangeWithEffect(player, PlayerInventoryTransaction.EventCreator.STANDARD)) { diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/network/ServerGamePacketListenerImplMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/network/ServerGamePacketListenerImplMixin_Vanilla.java index 916d1ab94a0..c6327084356 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/network/ServerGamePacketListenerImplMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/server/network/ServerGamePacketListenerImplMixin_Vanilla.java @@ -27,10 +27,10 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.network.protocol.game.ServerGamePacketListener; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.world.inventory.RecipeBookMenu; -import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.level.block.entity.BlockEntity; import org.checkerframework.checker.nullness.qual.NonNull; @@ -38,6 +38,7 @@ import org.spongepowered.api.item.inventory.crafting.CraftingInventory; import org.spongepowered.api.item.inventory.query.QueryTypes; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.accessor.world.inventory.AbstractFurnaceMenuAccessor; @@ -49,37 +50,40 @@ @Mixin(value = ServerGamePacketListenerImpl.class, priority = 999) public abstract class ServerGamePacketListenerImplMixin_Vanilla implements ServerGamePacketListener { + @Shadow public ServerPlayer player; + @SuppressWarnings({"rawtypes"}) - @WrapOperation(method = "lambda$handlePlaceRecipe$10", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/RecipeBookMenu;handlePlacement(ZLnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/server/level/ServerPlayer;)V")) - private void vanilla$onPlaceRecipe( - final RecipeBookMenu instance, - final boolean shift, - final RecipeHolder> recipe, - final ServerPlayer player, - final Operation original + @WrapOperation(method = "handlePlaceRecipe", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/RecipeBookMenu;handlePlacement(ZZLnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/player/Inventory;)Lnet/minecraft/world/inventory/RecipeBookMenu$PostPlaceAction;")) + private RecipeBookMenu.PostPlaceAction vanilla$onPlaceRecipe( + final RecipeBookMenu instance, final boolean shift, final boolean creative, + final RecipeHolder recipe, final ServerLevel serverLevel, + final net.minecraft.world.entity.player.Inventory inventory, + final Operation original ) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final var player = inventory.player; + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance(serverLevel).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); final var menu = (Inventory) player.containerMenu; - if ( menu instanceof AbstractFurnaceMenuAccessor afm && afm.accessor$container() instanceof BlockEntity be) { - try (final EffectTransactor ignored = transactor.logPlaceCookingRecipe(be,shift, recipe, player)) { - original.call(instance, shift, recipe, player); + if (menu instanceof AbstractFurnaceMenuAccessor afm && afm.accessor$container() instanceof BlockEntity be) { + try (final EffectTransactor ignored = transactor.logPlaceCookingRecipe(be, shift, recipe, this.player)) { + final var postPlaceAction = instance.handlePlacement(shift, creative, recipe, serverLevel, inventory); player.containerMenu.broadcastChanges(); - return; + return postPlaceAction; } } Inventory craftInv = menu.query(QueryTypes.INVENTORY_TYPE.get().of(CraftingInventory.class)); if (!(craftInv instanceof CraftingInventory)) { - original.call(instance, shift, recipe, player); + final var postPlaceAction = instance.handlePlacement(shift, creative, recipe, serverLevel, inventory); SpongeCommon.logger().warn("Detected crafting without a InventoryCrafting!? Crafting Event will not fire."); - return; + return postPlaceAction; } - try (final EffectTransactor ignored = transactor.logPlaceRecipe(shift, recipe, player, (CraftingInventory) craftInv)) { - original.call(instance, shift, recipe, player); + try (final EffectTransactor ignored = transactor.logPlaceRecipe(shift, (RecipeHolder) recipe, (ServerPlayer) player, (CraftingInventory) craftInv)) { + final var postPlaceAction = instance.handlePlacement(shift, creative, recipe, serverLevel, inventory); player.containerMenu.broadcastChanges(); + return postPlaceAction; } } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/tags/TagLoaderMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/tags/TagLoaderMixin_Vanilla.java index 237450ce7b1..c59dc87a2af 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/tags/TagLoaderMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/tags/TagLoaderMixin_Vanilla.java @@ -27,7 +27,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.tags.TagEntry; import net.minecraft.tags.TagLoader; import org.spongepowered.asm.mixin.Mixin; @@ -45,20 +45,20 @@ @Mixin(TagLoader.class) public abstract class TagLoaderMixin_Vanilla implements TagLoaderBridge { - @WrapOperation(method = "build(Lnet/minecraft/tags/TagEntry$Lookup;Ljava/util/List;)Lcom/mojang/datafixers/util/Either;", + @WrapOperation(method = "tryBuildTag(Lnet/minecraft/tags/TagEntry$Lookup;Ljava/util/List;)Lcom/mojang/datafixers/util/Either;", at = @At(value = "INVOKE", target = "Lnet/minecraft/tags/TagEntry;build(Lnet/minecraft/tags/TagEntry$Lookup;Ljava/util/function/Consumer;)Z")) protected boolean vanilla$onBuildTag(final TagEntry instance, final TagEntry.Lookup lookup, final Consumer consumer, final Operation original, final TagEntry.Lookup $$0, final List $$1, final @Local TagLoader.EntryWithSource entry) { return this.bridge$acceptTag(instance, lookup, consumer, original::call, $$1, entry); } - @Inject(method = "lambda$build$5", at = @At("HEAD")) - private void vanilla$onStartBuildingTag(final TagEntry.Lookup $$0x, final Map> $$1x, final ResourceLocation $$2x, + @Inject(method = "lambda$build$1", at = @At("HEAD")) + private void vanilla$onStartBuildingTag(final TagEntry.Lookup $$0x, final Map> $$1x, final Identifier $$2x, final @Coerce Object $$3x, final CallbackInfo ci) { this.bridge$buildingTagKey($$2x); } - @Inject(method = "lambda$build$5", at = @At("RETURN")) + @Inject(method = "lambda$build$1", at = @At("RETURN")) private void vanilla$onDoneBuildingTag(final CallbackInfo ci) { this.bridge$buildingTagKey(null); } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/EntityMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/EntityMixin_Vanilla.java index 2b76a635ef3..5631ca6b23e 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/EntityMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/EntityMixin_Vanilla.java @@ -24,10 +24,16 @@ */ package org.spongepowered.vanilla.mixin.core.world.entity; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.damagesource.DamageSources; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LightningBolt; import net.minecraft.world.level.Level; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.bridge.world.entity.EntityBridge; @Mixin(Entity.class) @@ -37,4 +43,18 @@ public abstract class EntityMixin_Vanilla implements EntityBridge { @Shadow public abstract Level shadow$level(); // @formatter:on + @Redirect(method = "thunderHit", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/damagesource/DamageSources;lightningBolt()Lnet/minecraft/world/damagesource/DamageSource;")) + private DamageSource vanilla$ThrowDamageEventWithLightingSource(final DamageSources sources, + final ServerLevel level, final LightningBolt lightningBolt + ) { + final var originalLightning = sources.lightningBolt(); + final var entitySource = org.spongepowered.api.event.cause.entity.damage.source.DamageSource.builder() + .from((org.spongepowered.api.event.cause.entity.damage.source.DamageSource) originalLightning) + .entity((org.spongepowered.api.entity.Entity) lightningBolt) + .build(); + return (DamageSource) (Object) entitySource; + } + } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/EntityTypeMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/EntityTypesMixin_Vanilla.java similarity index 93% rename from vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/EntityTypeMixin_Vanilla.java rename to vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/EntityTypesMixin_Vanilla.java index 532bd30c450..284f6c265cb 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/EntityTypeMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/EntityTypesMixin_Vanilla.java @@ -25,15 +25,15 @@ package org.spongepowered.vanilla.mixin.core.world.entity; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EntityTypes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.entity.SpongeEntityTypes; -@Mixin(EntityType.class) -public abstract class EntityTypeMixin_Vanilla { +@Mixin(EntityTypes.class) +public abstract class EntityTypesMixin_Vanilla { @Inject(method = "", at = @At("TAIL")) private static void impl$registerSpongeTypes(CallbackInfo ci) { diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Vanilla.java index 2e1fe7088ab..085a63ba57f 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Vanilla.java @@ -24,15 +24,31 @@ */ package org.spongepowered.vanilla.mixin.core.world.entity; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.cause.entity.MovementTypes; +import org.spongepowered.api.event.entity.MoveEntityEvent; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.event.ShouldFire; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.math.vector.Vector3d; @Mixin(value = LivingEntity.class) public abstract class LivingEntityMixin_Vanilla extends EntityMixin_Vanilla { + @Unique + @Nullable private Vector3d vanilla$preTeleportPosition; + @Inject( method = "updateFallFlying", at = @At( @@ -44,4 +60,44 @@ public abstract class LivingEntityMixin_Vanilla extends EntityMixin_Vanilla { protected void vanilla$onElytraUse(final CallbackInfo ci) { } + @Inject(method = "randomTeleport", at = @At("HEAD")) + private void vanilla$snapshotPositionBeforeVanillaTeleportLogic(final double x, final double y, final double z, final boolean changeState, + final CallbackInfoReturnable cir) { + final Entity self = (Entity) (Object) this; + this.vanilla$preTeleportPosition = new Vector3d(self.getX(), self.getY(), self.getZ()); + } + + @Inject(method = "randomTeleport", at = @At(value = "RETURN", ordinal = 0, shift = At.Shift.BY, by = 2), cancellable = true) + private void vanilla$callMoveEntityEventForTeleport(final double x, final double y, final double z, final boolean changeState, + final CallbackInfoReturnable cir) { + if (!ShouldFire.MOVE_ENTITY_EVENT) { + return; + } + + final Entity self = (Entity) (Object) this; + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { + frame.pushCause(this); + + // ENTITY_TELEPORT is our fallback context + if (!frame.currentContext().containsKey(EventContextKeys.MOVEMENT_TYPE)) { + frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.ENTITY_TELEPORT); + } + + final MoveEntityEvent event = SpongeEventFactory.createMoveEntityEvent(frame.currentCause(), + (org.spongepowered.api.entity.Entity) this, this.vanilla$preTeleportPosition, + new Vector3d(self.getX(), self.getY(), self.getZ()), + new Vector3d(x, y, z)); + + if (SpongeCommon.post(event)) { + self.teleportTo(this.vanilla$preTeleportPosition.x(), this.vanilla$preTeleportPosition.y(), + this.vanilla$preTeleportPosition.z()); + cir.setReturnValue(false); + return; + } + + self.teleportTo(event.destinationPosition().x(), event.destinationPosition().y(), + event.destinationPosition().z()); + } + } + } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Vanilla_Damage.java similarity index 52% rename from vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java rename to vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Vanilla_Damage.java index 0e03da3251f..cc0ae1270e8 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Vanilla_Damage.java @@ -24,48 +24,43 @@ */ package org.spongepowered.vanilla.mixin.core.world.entity; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.EntityReference; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.TamableAnimal; -import net.minecraft.world.entity.animal.Wolf; +import net.minecraft.world.entity.animal.wolf.Wolf; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Coerce; import org.spongepowered.asm.mixin.injection.Constant; import org.spongepowered.asm.mixin.injection.ModifyConstant; +import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.Redirect; - +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.common.bridge.world.entity.TrackedDamageBridge; @Mixin(value = LivingEntity.class, priority = 900) -public abstract class LivingEntityMixin_Attack_Impl { +public abstract class LivingEntityMixin_Vanilla_Damage implements TrackedDamageBridge { - @SuppressWarnings("InvalidInjectorMethodSignature") - @ModifyConstant(method = "hurt", constant = @Constant(classValue = Wolf.class, ordinal = 0)) - private Class attackImpl$onWolfCast(final Object entity, final Class wolf) { - return TamableAnimal.class; + @ModifyVariable(method = "actuallyHurt", at = @At("LOAD"), argsOnly = true, slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;awardStat(Lnet/minecraft/resources/Identifier;I)V"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getCombatTracker()Lnet/minecraft/world/damagesource/CombatTracker;"))) + private float damage$firePostEvent_Living(final float damage) { + return this.damage$firePostEvent(damage); } - @Redirect(method = "hurt", - at = @At(value = "INVOKE" , target = "Lnet/minecraft/world/entity/animal/Wolf;isTame()Z")) - private boolean attackImpl$onWolfIsTame(@Coerce final Object instance) { - return ((TamableAnimal)instance).isTame(); + @SuppressWarnings("InvalidInjectorMethodSignature") + @ModifyConstant(method = "resolvePlayerResponsibleForDamage", constant = @Constant(classValue = Wolf.class, ordinal = 0)) + private Class damage$onWolfCast(final Object entity, final Class wolf) { + return TamableAnimal.class; } - @Redirect(method = "hurt", - at = @At(value = "INVOKE" , target = "Lnet/minecraft/world/entity/animal/Wolf;getOwner()Lnet/minecraft/world/entity/LivingEntity;")) - private LivingEntity attackImpl$onWolfGetOwner(@Coerce final Object instance) { - return ((TamableAnimal)instance).getOwner(); + @Redirect(method = "resolvePlayerResponsibleForDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/wolf/Wolf;isTame()Z")) + private boolean damage$onWolfIsTame(@Coerce final Object instance) { + return ((TamableAnimal) instance).isTame(); } - /** - * Prevents {@link ServerPlayer#awardStat} from running before event - */ - @Redirect(method = "getDamageAfterMagicAbsorb", - at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;awardStat(Lnet/minecraft/resources/ResourceLocation;I)V")) - public void attackImpl$onAwardStatDamageResist(final ServerPlayer instance, final ResourceLocation resourceLocation, final int i) { - // do nothing + @Redirect(method = "resolvePlayerResponsibleForDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/wolf/Wolf;getOwnerReference()Lnet/minecraft/world/entity/EntityReference;")) + private EntityReference damage$onWolfGetOwner(@Coerce final Object instance) { + return ((TamableAnimal) instance).getOwnerReference(); } - - } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/animal/golem/SnowGolemMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/animal/golem/SnowGolemMixin_Vanilla.java new file mode 100644 index 00000000000..23198ebae2e --- /dev/null +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/animal/golem/SnowGolemMixin_Vanilla.java @@ -0,0 +1,44 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.mixin.core.world.entity.animal.golem; + + +import net.minecraft.world.entity.animal.golem.SnowGolem; +import net.minecraft.world.level.gamerules.GameRule; +import net.minecraft.world.level.gamerules.GameRules; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.bridge.world.entity.GrieferBridge; + +@Mixin(SnowGolem.class) +public abstract class SnowGolemMixin_Vanilla { + + @Redirect(method = "aiStep()V", at = @At( + value = "INVOKE", target = "Lnet/minecraft/world/level/gamerules/GameRules;get(Lnet/minecraft/world/level/gamerules/GameRule;)Ljava/lang/Object;")) + private Object impl$checkCanGrief(final GameRules gameRules, final GameRule rule) { + return (Boolean) gameRules.get(rule) && ((GrieferBridge) this).bridge$canGrief(); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Shared_Attack_Impl.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/player/PlayerMixin_Vanilla_Damage.java similarity index 62% rename from src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Shared_Attack_Impl.java rename to vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/player/PlayerMixin_Vanilla_Damage.java index fb7aceaf7b3..927f3c36760 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Shared_Attack_Impl.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/player/PlayerMixin_Vanilla_Damage.java @@ -22,29 +22,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.core.world.entity.player; +package org.spongepowered.vanilla.mixin.core.world.entity.player; import net.minecraft.world.entity.player.Player; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.common.mixin.core.world.entity.LivingEntityMixin_Attack_Impl; +import org.spongepowered.vanilla.mixin.core.world.entity.LivingEntityMixin_Vanilla_Damage; -// Forge and Vanilla -@Mixin(value = Player.class, priority = 900) -public abstract class PlayerMixin_Shared_Attack_Impl extends LivingEntityMixin_Attack_Impl { +@Mixin(Player.class) +public abstract class PlayerMixin_Vanilla_Damage extends LivingEntityMixin_Vanilla_Damage { - /** - * Set absorbed damage after calling {@link Player#setAbsorptionAmount} in which we called the event - */ - @ModifyVariable(method = "actuallyHurt", ordinal = 2, - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;setAbsorptionAmount(F)V")), - at = @At(value = "STORE", ordinal = 0)) - public float attackImpl$setAbsorbed(final float value) { - if (this.attackImpl$actuallyHurtResult.event().isCancelled()) { - return 0; - } - return this.attackImpl$actuallyHurtResult.damageAbsorbed().orElse(0f); + @ModifyVariable(method = "actuallyHurt", at = @At("LOAD"), argsOnly = true, slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;awardStat(Lnet/minecraft/resources/Identifier;I)V"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;causeFoodExhaustion(F)V"))) + private float damage$firePostEvent_Player(final float damage) { + return this.damage$firePostEvent(damage); } } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/vehicle/BoatMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/vehicle/boat/AbstractBoatMixin_Vanilla.java similarity index 88% rename from vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/vehicle/BoatMixin_Vanilla.java rename to vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/vehicle/boat/AbstractBoatMixin_Vanilla.java index 2e60f5aea1f..fddc1321515 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/vehicle/BoatMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/vehicle/boat/AbstractBoatMixin_Vanilla.java @@ -22,18 +22,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.vanilla.mixin.core.world.entity.vehicle; +package org.spongepowered.vanilla.mixin.core.world.entity.vehicle.boat; -import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.entity.vehicle.boat.AbstractBoat; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.common.bridge.world.entity.vehicle.BoatBridge; +import org.spongepowered.common.bridge.world.entity.vehicle.AbstractBoatBridge; -@Mixin(Boat.class) -public abstract class BoatMixin_Vanilla implements BoatBridge { +@Mixin(AbstractBoat.class) +public abstract class AbstractBoatMixin_Vanilla implements AbstractBoatBridge { /** * Forge changes this check to ask the block state for "slipperiness" so we return the check here to Vanilla diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/commands/SpreadPlayersCommandMixin_Neo.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/item/TeleportRandomlyConsumeEffectMixin_Vanilla.java similarity index 65% rename from neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/commands/SpreadPlayersCommandMixin_Neo.java rename to vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/item/TeleportRandomlyConsumeEffectMixin_Vanilla.java index 4dea593820d..a2fdc00715b 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/commands/SpreadPlayersCommandMixin_Neo.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/item/TeleportRandomlyConsumeEffectMixin_Vanilla.java @@ -22,12 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.neoforge.mixin.core.server.commands; +package org.spongepowered.vanilla.mixin.core.world.item; -import net.minecraft.server.commands.SpreadPlayersCommand; -import net.minecraft.world.entity.Entity; -import net.neoforged.neoforge.event.EventHooks; -import net.neoforged.neoforge.event.entity.EntityTeleportEvent; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.cause.entity.MovementTypes; @@ -36,18 +34,16 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.event.tracking.PhaseTracker; -@Mixin(SpreadPlayersCommand.class) -public abstract class SpreadPlayersCommandMixin_Neo { +@Mixin(TeleportRandomlyConsumeEffect.class) +public abstract class TeleportRandomlyConsumeEffectMixin_Vanilla { - @Redirect(method = "setPlayerPositions", at = @At( - value = "INVOKE", - target = "Lnet/neoforged/neoforge/event/EventHooks;onEntityTeleportSpreadPlayersCommand(Lnet/minecraft/world/entity/Entity;DDD)Lnet/neoforged/neoforge/event/entity/EntityTeleportEvent$SpreadPlayersCommand;" - )) - private static EntityTeleportEvent.SpreadPlayersCommand vanilla$createCauseFrameForTeleport(Entity entity, double targetX, double targetY, double targetZ) { + @Redirect(method = "apply", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;randomTeleport(DDDZ)Z")) + private boolean vanilla$createCauseFrameForTeleport(final LivingEntity entity, final double x, final double y, final double z, + final boolean changeState) { try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.COMMAND); + frame.addContext(EventContextKeys.MOVEMENT_TYPE, MovementTypes.CHORUS_FRUIT.get()); - return EventHooks.onEntityTeleportSpreadPlayersCommand(entity, targetX, targetY, targetZ); + return entity.randomTeleport(x, y, z, changeState); } } } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/ServerExplosionMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/ServerExplosionMixin_Vanilla.java new file mode 100644 index 00000000000..28606236790 --- /dev/null +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/ServerExplosionMixin_Vanilla.java @@ -0,0 +1,115 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.mixin.core.world.level; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.ServerExplosion; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.world.ExplosionEvent; +import org.spongepowered.api.world.explosion.Explosion; +import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.api.world.server.ServerWorld; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.event.ShouldFire; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.util.VecHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@Mixin(ServerExplosion.class) +public abstract class ServerExplosionMixin_Vanilla { + + + // @formatter:off + @Shadow @Final private ServerLevel level; + + @Shadow protected abstract boolean shadow$interactsWithBlocks(); + // @formatter:on + + /** + * These two fields are used in the mixin below, but require a redirect specific for SpongeVanilla + * and SpongeForge as NeoForge makes a change to the method being used here. + * + * @see org.spongepowered.common.mixin.core.world.level.ServerExplosionMixin + */ + private boolean impl$shouldDamageEntities; + private double impl$knockbackMultiplier; + + private List impl$affectedBlocks; + + @Redirect(method = "hurtEntities", at = @At(value = "INVOKE", + target = "Lnet/minecraft/server/level/ServerLevel;getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;")) + private List vanilla$throwEventToFilterEntitiesOnHurt(final ServerLevel instance, final Entity sourceEntity, final AABB aabb) { + final List entities; + if (this.impl$shouldDamageEntities) { + // filter out invulnerable entities before event + entities = instance.getEntities(sourceEntity, aabb).stream() + .filter(e -> !e.ignoreExplosion((net.minecraft.world.level.Explosion) this)) + .toList(); + } else { + entities = Collections.emptyList(); + } + + if (ShouldFire.EXPLOSION_EVENT_DETONATE) { + final var apiWorld = (ServerWorld) this.level; + final var apiEntities = entities.stream().map(org.spongepowered.api.entity.Entity.class::cast).collect(Collectors.toCollection(ArrayList::new)); + final var apiBlockPositions = this.impl$affectedBlocks.stream().map(bp -> ServerLocation.of(apiWorld, VecHelper.toVector3i(bp))).toList(); + final Cause cause = PhaseTracker.getInstance().currentCause(); + final ExplosionEvent.Detonate event = SpongeEventFactory.createExplosionEventDetonate(cause, apiBlockPositions, apiEntities, (Explosion) this, apiWorld); + if (SpongeCommon.post(event)) { + this.impl$affectedBlocks.clear(); // no blocks affected + return Collections.emptyList(); // no entities affected + } + if (this.shadow$interactsWithBlocks()) { + this.impl$affectedBlocks = event.affectedLocations().stream().map(VecHelper::toBlockPos).collect(Collectors.toList()); + } + if (this.impl$shouldDamageEntities) { + return event.entities().stream().map(Entity.class::cast).toList(); + } + } + return entities; + } + + @WrapOperation(method = "hurtEntities",at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/Vec3;scale(D)Lnet/minecraft/world/phys/Vec3;")) + private Vec3 vanilla$setKnockbackMultiplier(Vec3 instance, double xVel, Operation original) { + return original.call(instance, xVel * this.impl$knockbackMultiplier); + } + +} diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/block/FireBlockMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/block/FireBlockMixin_Vanilla.java index 01c0191fa31..390e94c0881 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/block/FireBlockMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/block/FireBlockMixin_Vanilla.java @@ -48,7 +48,7 @@ public class FireBlockMixin_Vanilla { cancellable = true) private void impl$onCatchFirePreCheck( final Level world, final BlockPos pos, final int chance, final RandomSource random, final int age, final CallbackInfo callbackInfo) { - if (!world.isClientSide) { + if (!world.isClientSide()) { if (ShouldFire.CHANGE_BLOCK_EVENT_PRE && SpongeCommonEventFactory.callChangeBlockEventPre((ServerLevelBridge) world, pos).isCancelled()) { callbackInfo.cancel(); } @@ -64,7 +64,7 @@ public class FireBlockMixin_Vanilla { cancellable = true) private void impl$onCatchFirePreCheckOther( final Level world, final BlockPos pos, final int chance, final RandomSource random, final int age, final CallbackInfo callbackInfo) { - if (!world.isClientSide) { + if (!world.isClientSide()) { if (ShouldFire.CHANGE_BLOCK_EVENT_PRE && SpongeCommonEventFactory.callChangeBlockEventPre((ServerLevelBridge) world, pos).isCancelled()) { callbackInfo.cancel(); } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Vanilla.java index 7b0d140bf6e..c4a080c52e4 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Vanilla.java @@ -24,16 +24,18 @@ */ package org.spongepowered.vanilla.mixin.core.world.level.block.entity; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; -import net.minecraft.core.RegistryAccess; +import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.AbstractCookingRecipe; import net.minecraft.world.item.crafting.RecipeHolder; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; import net.minecraft.world.level.block.state.BlockState; -import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.Opcodes; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.block.entity.carrier.furnace.FurnaceBlockEntity; @@ -49,10 +51,12 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.block.entity.AbstractFurnaceBlockEntityBridge; import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.event.tracking.phase.general.GeneralPhase; +import org.spongepowered.common.event.tracking.phase.general.RecipeContext; import org.spongepowered.common.inventory.adapter.impl.slots.SlotAdapter; import org.spongepowered.common.item.util.ItemStackUtil; import org.spongepowered.common.mixin.core.world.level.block.entity.BaseContainerBlockEntityMixin; @@ -67,57 +71,98 @@ public abstract class AbstractFurnaceBlockEntityMixin_Vanilla extends BaseContai // @formatter:off @Shadow protected NonNullList items; - @Shadow int cookingProgress; - @Shadow private static boolean canBurn(final RegistryAccess registryAccess, @Nullable final RecipeHolder recipe, final NonNullList slots, final int maxStackSize) { - throw new UnsupportedOperationException("Shadowed canBurn"); - } + @Shadow private int cookingTimer; // @formatter:on + private static final ThreadLocal switched = new ThreadLocal<>(); private boolean vanilla$filledWaterBucket; // Tick up and Start - @Redirect(method = "serverTick", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;canBurn(Lnet/minecraft/core/RegistryAccess;Lnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/core/NonNullList;I)Z", - ordinal = 1)) - private static boolean vanilla$checkIfCanSmelt(final RegistryAccess registryAccess, @Nullable final RecipeHolder recipe, final NonNullList slots, final int maxStackSize, final Level level, final BlockPos entityPos, final BlockState state, final AbstractFurnaceBlockEntity entityIn) { - if (!AbstractFurnaceBlockEntityMixin_Vanilla.canBurn(registryAccess, recipe, slots, maxStackSize)) { + @WrapOperation(method = "serverTick", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;canBurn(Lnet/minecraft/core/NonNullList;ILnet/minecraft/world/item/ItemStack;)Z" + ) + ) + private static boolean vanilla$checkIfCanSmelt( + final NonNullList items, + final int maxStackSize, + final ItemStack burnResult, + final Operation original, + final ServerLevel level, + final BlockPos pos, + final BlockState state, + final AbstractFurnaceBlockEntity entityIn, + final @Local RecipeHolder recipe + ) { + if (!original.call(items, maxStackSize, burnResult)) { return false; } - final var entity = (AbstractFurnaceBlockEntityMixin_Vanilla) (Object) (entityIn); - final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(slots.get(1)); + final var cookingRecipe = recipe.value(); + final var entity = (AbstractFurnaceBlockEntityMixin_Vanilla) (Object) (entityIn); + final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(items.get(1)); final Cause cause = PhaseTracker.getInstance().currentCause(); - if (entity.cookingProgress == 0) { // Start + final var identifier = recipe.id().identifier(); + // Prepare a phase stack to capture the recipe in use + final var context = GeneralPhase.State.RECIPE_CRAFTING.createPhaseContext(PhaseTracker.getInstance()) + .recipe(recipe.value()) + .recipeID(identifier) + .furnace(entityIn) + .provideItem(items.get(1)) + .buildAndSwitch(); + switched.set(context); + if (entity.cookingTimer == 0) { // Start final CookingEvent.Start event = SpongeEventFactory.createCookingEventStart(cause, (FurnaceBlockEntity) entityIn, Optional.of(fuel), - Optional.of((CookingRecipe) recipe.value()), Optional.of((ResourceKey) (Object) recipe.id())); + Optional.of((CookingRecipe) cookingRecipe), Optional.of((ResourceKey) (Object) identifier)); SpongeCommon.post(event); return !event.isCancelled(); } else { // Tick up final ItemStackSnapshot cooking = ItemStackUtil.snapshotOf(entity.items.get(0)); final CookingEvent.Tick event = SpongeEventFactory.createCookingEventTick(cause, (FurnaceBlockEntity) entityIn, cooking, Optional.of(fuel), - Optional.of((CookingRecipe) recipe.value()), Optional.of((ResourceKey) (Object) recipe.id())); + Optional.of((CookingRecipe) cookingRecipe), Optional.of((ResourceKey) (Object) identifier)); SpongeCommon.post(event); return !event.isCancelled(); } } + @Inject(method = "serverTick", at = @At("RETURN")) + private static void vanilla$closeRecipeContext( + final ServerLevel level, + final BlockPos pos, final BlockState state, + final AbstractFurnaceBlockEntity entity, + final CallbackInfo ci) { + if (switched.get() != null && PhaseTracker.getInstance().getPhaseContext() != switched.get()) { + SpongeCommon.logger().warn("Closed a recipe context that wasn't open!"); + } + final var closing = switched.get(); + switched.remove(); + if (closing != null) { + closing.close(); + } + } + // Tick down @Redirect(method = "serverTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;clamp(III)I")) - private static int vanilla$resetCookTimeIfCancelled(final int newCookTime, final int zero, final int totalCookTime, - final Level level, final BlockPos entityPos, final BlockState state, final AbstractFurnaceBlockEntity entityIn) { + private static int vanilla$resetCookTimeIfCancelled( + final int newCookTime, final int zero, final int totalCookTime, + final ServerLevel level, final BlockPos entityPos, final BlockState state, + final AbstractFurnaceBlockEntity entityIn) { final int clampedCookTime = Mth.clamp(newCookTime, zero, totalCookTime); final var entity = (AbstractFurnaceBlockEntityMixin_Vanilla) (Object) entityIn; - final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(entity.items.get(1)); + if (PhaseTracker.getInstance().getPhaseContext() != switched.get()) { + // we're in an invariant state + return clampedCookTime; + } + final var fuel = switched.get().usedItem(); final Cause cause = PhaseTracker.getInstance().currentCause(); - final var recipe = entity.bridge$getCurrentRecipe(); + final var recipe = switched.get().recipe(); final ItemStackSnapshot cooking = ItemStackUtil.snapshotOf(entity.items.get(0)); final CookingEvent.Tick event = SpongeEventFactory.createCookingEventTick(cause, (FurnaceBlockEntity) entityIn, cooking, Optional.of(fuel), - recipe.map(r -> (CookingRecipe) r.value()), recipe.map(r -> (ResourceKey) (Object) r.id())); + Optional.of((CookingRecipe) recipe), Optional.of((ResourceKey) (Object) switched.get().recipeID())); SpongeCommon.post(event); if (event.isCancelled()) { - return entity.cookingProgress; // dont tick down + return entity.cookingTimer; // dont tick down } return clampedCookTime; @@ -126,9 +171,11 @@ public abstract class AbstractFurnaceBlockEntityMixin_Vanilla extends BaseContai // Finish @Inject(method = "burn", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/NonNullList;set(ILjava/lang/Object;)Ljava/lang/Object;"), slice = @Slice( - from = @At(value = "FIELD", target = "Lnet/minecraft/world/level/block/Blocks;WET_SPONGE:Lnet/minecraft/world/level/block/Block;", opcode = Opcodes.GETSTATIC) - )) - private static void vanilla$captureBucketFill(final RegistryAccess registryAccess, final RecipeHolder recipe, final NonNullList slots, final int maxStackSize, final CallbackInfoReturnable cir) { + from = @At(value = "FIELD", target = "Lnet/minecraft/world/item/Items;WET_SPONGE:Lnet/minecraft/world/item/Item;", opcode = Opcodes.GETSTATIC) + )) + private static void vanilla$captureBucketFill( + final NonNullList items, final ItemStack inputItemStack, final ItemStack result, final CallbackInfo ci + ) { final Cause cause = PhaseTracker.getInstance().currentCause(); final FurnaceBlockEntity entity = cause.first(FurnaceBlockEntity.class) .orElseThrow(() -> new IllegalStateException("Expected to have a FurnaceBlockEntity in the Cause")); @@ -136,15 +183,20 @@ public abstract class AbstractFurnaceBlockEntityMixin_Vanilla extends BaseContai } @Inject(method = "burn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;shrink(I)V", shift = At.Shift.AFTER)) - private static void vanilla$afterSmeltItem(final RegistryAccess registryAccess, final RecipeHolder recipe, final NonNullList slots, final int maxStackSize, final CallbackInfoReturnable cir) { - final ItemStack itemIn = slots.get(0); - final ItemStack recipeResult = recipe.value().getResultItem(registryAccess); - final ItemStack itemOut = slots.get(2); + // final RegistryAccess registryAccess, final RecipeHolder recipe, final NonNullList slots, final int var2, final CallbackInfoReturnable cir + private static void vanilla$afterSmeltItem( + final NonNullList items, final ItemStack inputItemStack, final ItemStack result, final CallbackInfo ci + ) { + final ItemStack itemIn = items.get(0); + final ItemStack itemOut = items.get(2); + if (PhaseTracker.getInstance().getPhaseContext() != switched.get()) { + return; + } final Cause cause = PhaseTracker.getInstance().currentCause(); - final FurnaceBlockEntity entity = cause.first(FurnaceBlockEntity.class) - .orElseThrow(() -> new IllegalStateException("Expected to have a FurnaceBlockEntity in the Cause")); - + final FurnaceBlockEntity entity = (FurnaceBlockEntity) switched.get().furnace(); + final var recipe = switched.get().recipe(); + final var recipeID = switched.get().recipeID(); final List transactions = new ArrayList<>(); itemIn.grow(1); final ItemStackSnapshot originalSmeltItem = ItemStackUtil.snapshotOf(itemIn); @@ -153,25 +205,37 @@ public abstract class AbstractFurnaceBlockEntityMixin_Vanilla extends BaseContai final boolean hasFuel = !((AbstractFurnaceBlockEntityMixin_Vanilla) entity).vanilla$filledWaterBucket; if (((AbstractFurnaceBlockEntityMixin_Vanilla) entity).vanilla$filledWaterBucket) { - transactions.add(new SlotTransaction(entity.inventory().slot(1).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(slots.get(1)))); + transactions.add(new SlotTransaction(entity.inventory().slot(1).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(items.get(1)))); } ((AbstractFurnaceBlockEntityMixin_Vanilla) entity).vanilla$filledWaterBucket = false; if (itemOut.isEmpty()) { - transactions.add(new SlotTransaction(entity.inventory().slot(2).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(recipeResult))); - } else if (ItemStack.isSameItemSameComponents(itemOut, recipeResult)) { + transactions.add(new SlotTransaction(entity.inventory().slot(2).get(), ItemStackSnapshot.empty(), ItemStackUtil.snapshotOf(result))); + } else if (ItemStack.isSameItemSameComponents(itemOut, result)) { itemOut.shrink(1); final ItemStackSnapshot originalResult = ItemStackUtil.snapshotOf(itemOut); itemOut.grow(1); transactions.add(new SlotTransaction(entity.inventory().slot(2).get(), originalResult, ItemStackUtil.snapshotOf(itemOut))); } - final Optional fuel = hasFuel && !slots.get(1).isEmpty() ? Optional.of(ItemStackUtil.snapshotOf(slots.get(1))) : Optional.empty(); + final Optional fuel = hasFuel && !items.get(1).isEmpty() ? Optional.of(ItemStackUtil.snapshotOf(items.get(1))) : Optional.empty(); final CookingEvent.Finish event = SpongeEventFactory.createCookingEventFinish(cause, entity, - fuel, Optional.of((CookingRecipe) recipe.value()), Optional.of((ResourceKey) (Object) recipe.id()), Collections.unmodifiableList(transactions)); + fuel, Optional.of((CookingRecipe) recipe), Optional.of((ResourceKey) (Object) recipeID), Collections.unmodifiableList(transactions)); SpongeCommon.post(event); for (final SlotTransaction transaction : transactions) { - transaction.custom().ifPresent(item -> slots.set(((SlotAdapter) transaction.slot()).getOrdinal(), ItemStackUtil.fromSnapshotToNative(item))); + transaction.custom().ifPresent(item -> items.set(((SlotAdapter) transaction.slot()).getOrdinal(), ItemStackUtil.fromSnapshotToNative(item))); } } + + // Interrupt-Active - e.g. a player removing the currently smelting item + @Inject( + method = "setItem", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;getTotalCookTime(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;)I" + ) + ) + private void vanilla$interruptSmelt(final CallbackInfo ci) { + this.impl$callInterruptSmeltEvent(); + } } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/storage/LevelStorageSourceMixin_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/storage/LevelStorageSourceMixin_Vanilla.java index 9c101735a68..bf239b3c537 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/storage/LevelStorageSourceMixin_Vanilla.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/level/storage/LevelStorageSourceMixin_Vanilla.java @@ -24,54 +24,14 @@ */ package org.spongepowered.vanilla.mixin.core.world.level.storage; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.mojang.serialization.Dynamic; -import com.mojang.serialization.Lifecycle; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; -import net.minecraft.nbt.Tag; -import net.minecraft.world.level.LevelSettings; -import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.PrimaryLevelData; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.common.bridge.world.level.storage.PrimaryLevelDataBridge; +/** + * Previously captured and read SpongeData from level.dat during world loading. + * SpongeData is no longer stored in level.dat; per-world identity (UUID, key) + * is now handled by {@code SpongeRegistryData} via per-dimension SavedDataStorage. + */ @Mixin(LevelStorageSource.class) public abstract class LevelStorageSourceMixin_Vanilla { - - private static Dynamic impl$spongeLevelData; - - @WrapOperation( - method = "readLevelDataTagFixed", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/nbt/CompoundTag;getCompound(Ljava/lang/String;)Lnet/minecraft/nbt/CompoundTag;", - ordinal = 0 - ) - ) - private static CompoundTag impl$createSpongeLevelData(final CompoundTag compoundNBT, final String path, final Operation original) { - LevelStorageSourceMixin_Vanilla.impl$spongeLevelData = new Dynamic<>(NbtOps.INSTANCE, compoundNBT); - return original.call(compoundNBT, path); - } - - @WrapOperation( - method = "getLevelDataAndDimensions", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/level/storage/PrimaryLevelData;parse(Lcom/mojang/serialization/Dynamic;Lnet/minecraft/world/level/LevelSettings;Lnet/minecraft/world/level/storage/PrimaryLevelData$SpecialWorldProperty;Lnet/minecraft/world/level/levelgen/WorldOptions;Lcom/mojang/serialization/Lifecycle;)Lnet/minecraft/world/level/storage/PrimaryLevelData;" - ) - ) - private static PrimaryLevelData impl$readSpongeLevelData(final Dynamic dynamic, final LevelSettings settings, - final PrimaryLevelData.SpecialWorldProperty special, final WorldOptions options, final Lifecycle lifecycle, final Operation original) - { - final PrimaryLevelData levelData = original.call(dynamic, settings, special, options, lifecycle); - - ((PrimaryLevelDataBridge) levelData).bridge$readSpongeLevelData(LevelStorageSourceMixin_Vanilla.impl$spongeLevelData); - - LevelStorageSourceMixin_Vanilla.impl$spongeLevelData = null; - return levelData; - } } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory_Vanilla.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory_Vanilla.java deleted file mode 100644 index e869ae9ca9f..00000000000 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/inventory/event/world/level/block/entity/HopperBlockEntityMixin_Inventory_Vanilla.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.vanilla.mixin.inventory.event.world.level.block.entity; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.Container; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.HopperBlockEntity; -import org.spongepowered.api.item.inventory.Inventory; -import org.spongepowered.api.item.inventory.transaction.SlotTransaction; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import org.spongepowered.common.accessor.world.level.block.entity.HopperBlockEntityAccessor; -import org.spongepowered.common.bridge.world.inventory.container.TrackedInventoryBridge; -import org.spongepowered.common.event.ShouldFire; -import org.spongepowered.common.event.inventory.InventoryEventFactory; -import org.spongepowered.common.inventory.util.InventoryUtil; - -@Mixin(HopperBlockEntity.class) -public abstract class HopperBlockEntityMixin_Inventory_Vanilla { - - @Redirect(method = "ejectItems", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;isFullContainer(Lnet/minecraft/world/Container;Lnet/minecraft/core/Direction;)Z")) - private static boolean vanilla$throwTransferPreIfNotFull( - final Container attachedContainer, final Direction direction, final Level level, final BlockPos pos, final HopperBlockEntity hopper) { - final boolean result = HopperBlockEntityAccessor.invoker$isFullContainer(attachedContainer, direction); - if (result || !ShouldFire.TRANSFER_INVENTORY_EVENT_PRE) { - return result; - } - return InventoryEventFactory.callTransferPre(InventoryUtil.toInventory(hopper), InventoryUtil.toInventory(attachedContainer)).isCancelled(); - } - - - // Post Captured Transactions - - @Inject(method = "ejectItems", locals = LocalCapture.CAPTURE_FAILEXCEPTION, - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z", - ordinal = 1)) - private static void vanilla$afterPutStackInSlots( - final Level var0, final BlockPos var1, final HopperBlockEntity var3, - final CallbackInfoReturnable cir, final Container iInventory, final Direction enumFacing, - final int i, final ItemStack itemStack, final int count, final ItemStack itemStack1 - ) { - // after putStackInInventoryAllSlots if the transfer worked - if (ShouldFire.TRANSFER_INVENTORY_EVENT_POST && itemStack1.isEmpty()) { - // Capture Insert in Origin - final TrackedInventoryBridge capture = InventoryUtil.forCapture(var3); - int newCount = itemStack.getCount(); - itemStack.setCount(count); - var originalStack = itemStack.copy(); - itemStack.setCount(newCount); - final SlotTransaction sourceSlotTransaction = InventoryEventFactory.captureTransaction(capture, (Inventory) var3, i, originalStack); - // Call event - InventoryEventFactory.callTransferPost(capture, (Inventory) iInventory, InventoryUtil.toInventory(iInventory), originalStack, sourceSlotTransaction); - } - - InventoryUtil.updateInventoryNoEvents(var3); - InventoryUtil.updateInventoryNoEvents(iInventory); - } - -} diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/server/level/ServerPlayerGameModeMixin_Vanilla_Tracker.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/server/level/ServerPlayerGameModeMixin_Vanilla_Tracker.java index 54d359b533e..477a99368df 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/server/level/ServerPlayerGameModeMixin_Vanilla_Tracker.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/server/level/ServerPlayerGameModeMixin_Vanilla_Tracker.java @@ -25,6 +25,7 @@ package org.spongepowered.vanilla.mixin.tracker.server.level; import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; import net.minecraft.world.entity.player.Player; @@ -55,7 +56,7 @@ public abstract class ServerPlayerGameModeMixin_Vanilla_Tracker { @Redirect(method = "destroyBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;mineBlock(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/player/Player;)V")) public void vanillaTracker$onMineBlock(final ItemStack itemStack, final Level param0, final BlockState param1, final BlockPos param2, final Player param3) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final PhaseContext<@NonNull ?> context = PhaseTracker.getWorldInstance((ServerLevel) param0).getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); itemStack.mineBlock(param0, param1, param2, param3); // Needs to get logged as a sideeffect under the BlockChange diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/world/level/block/BlocksMixin_Vanilla_Tracker.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/world/level/block/BlocksMixin_Vanilla_Tracker.java index bee1fba6587..eaf69d196bd 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/world/level/block/BlocksMixin_Vanilla_Tracker.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/world/level/block/BlocksMixin_Vanilla_Tracker.java @@ -24,34 +24,34 @@ */ package org.spongepowered.vanilla.mixin.tracker.world.level.block; -import net.minecraft.resources.ResourceKey; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.storage.loot.LootTable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.accessor.world.level.block.state.BlockBehaviourAccessor; import org.spongepowered.common.bridge.RegistryBackedTrackableBridge; @Mixin(Blocks.class) public abstract class BlocksMixin_Vanilla_Tracker { - @Redirect(method = "", + @Inject(method = "", at = @At( - value = "INVOKE", + value = "RETURN", target = "Lnet/minecraft/world/level/block/Block;getLootTable()Lnet/minecraft/resources/ResourceKey;" ) ) - private static ResourceKey impl$initializeTrackerState(final Block block) { - final boolean randomlyTicking = ((BlockBehaviourAccessor) block).invoker$isRandomlyTicking(block.defaultBlockState()); + private static void impl$initializeTrackerState(final CallbackInfo ci) { + for (final Block block : BuiltInRegistries.BLOCK) { + final boolean randomlyTicking = ((BlockBehaviourAccessor) block).invoker$isRandomlyTicking(block.defaultBlockState()); - // TODO Not the best check but the tracker options only matter during block ticks... - if (randomlyTicking) { - final var trackableBridge = (RegistryBackedTrackableBridge) block; - trackableBridge.bridge$refreshTrackerStates(); + // TODO Not the best check but the tracker options only matter during block ticks... + if (randomlyTicking) { + final var trackableBridge = (RegistryBackedTrackableBridge) block; + trackableBridge.bridge$refreshTrackerStates(); + } } - - return block.getLootTable(); } } diff --git a/vanilla/src/mixins/resources/mixins.spongevanilla.core.json b/vanilla/src/mixins/resources/mixins.spongevanilla.core.json index f8c7de8a92b..7df7d999a87 100644 --- a/vanilla/src/mixins/resources/mixins.spongevanilla.core.json +++ b/vanilla/src/mixins/resources/mixins.spongevanilla.core.json @@ -1,46 +1,55 @@ { - "required": true, - "parent": "mixins.sponge.parent.json", - "package": "org.spongepowered.vanilla.mixin.core", - "priority": 1301, - "client": [ - "client.ClientBrandRetrieverMixin_Vanilla", - "client.MinecraftMixin_Vanilla", - "client.gui.screens.TitleScreenMixin_Vanilla", - "client.renderer.entity.EntityRenderDispatcherMixin_Vanilla", - "client.server.IntegratedServerMixin_Vanilla" - ], - "server": [ - "server.dedicated.DedicatedServer_1_Mixin_Vanilla", - "server.dedicated.DedicatedServerMixin_Vanilla" - ], - "mixins": [ - "CrashReportMixin_Vanilla", - "commands.CommandsMixin_Vanilla", - "commands.arguments.selector.EntitySelectorMixin_Vanilla", - "network.ConnectionMixin_Vanilla", - "network.protocol.common.ClientboundCustomPayloadPacketMixin_Vanilla", - "network.protocol.common.ServerboundCustomPayloadPacketMixin_Vanilla", - "server.BootstrapMixin_Vanilla", - "server.MinecraftServerMixin_Vanilla", - "server.commands.SpreadPlayersCommandMixin_Vanilla", - "server.level.ServerPlayerMixin_Vanilla", - "server.network.ServerGamePacketListenerImplMixin_Vanilla", - "server.packs.repository.PackRepositoryMixin_Vanilla", - "tags.TagLoaderMixin_Vanilla", - "world.entity.EntityMixin_Vanilla", - "world.entity.EntityTypeMixin_Vanilla", - "world.entity.LivingEntityMixin_Attack_Impl", - "world.entity.LivingEntityMixin_Vanilla", - "world.entity.ai.attributes.DefaultAttributesMixin", - "world.entity.item.ItemEntityMixin_Vanilla", - "world.entity.player.PlayerMixin_Vanilla_Attack_Impl", - "world.entity.vehicle.BoatMixin_Vanilla", - "world.level.block.FireBlockMixin_Vanilla", - "world.level.block.entity.AbstractFurnaceBlockEntityMixin_Vanilla", - "world.level.storage.LevelStorageSourceMixin_Vanilla" - ], - "overwrites": { - "conformVisibility": true - } + "required": true, + "parent": "mixins.sponge.parent.json", + "package": "org.spongepowered.vanilla.mixin.core", + "priority": 1301, + "injectors": { + "maxShiftBy": 3 + }, + "client": [ + "client.ClientBrandRetrieverMixin_Vanilla", + "client.MinecraftMixin_Vanilla", + "client.gui.screens.TitleScreenMixin_Vanilla", + "client.renderer.entity.EntityRenderDispatcherMixin_Vanilla", + "client.server.IntegratedServerMixin_Vanilla" + ], + "server": [ + "server.dedicated.DedicatedServer_1_Mixin_Vanilla", + "server.dedicated.DedicatedServerMixin_Vanilla" + ], + "mixins": [ + "CrashReportMixin_Vanilla", + "commands.CommandsMixin_Vanilla", + "commands.arguments.selector.EntitySelectorMixin_Vanilla", + "network.ConnectionMixin_Vanilla", + "network.protocol.common.ClientboundCustomPayloadPacketMixin_Vanilla", + "network.protocol.common.ServerboundCustomPayloadPacketMixin_Vanilla", + "registries.BuiltInRegistriesMixin_Vanilla", + "resources.RegistryDataLoaderMixin_Vanilla", + "server.BootstrapMixin_Vanilla", + "server.MinecraftServerMixin_Vanilla", + "server.ReloadableServerResourcesMixin_Vanilla", + "server.level.ServerLevelMixin_Vanilla", + "server.level.ServerPlayerMixin_Vanilla", + "server.network.ServerGamePacketListenerImplMixin_Vanilla", + "server.packs.repository.PackRepositoryMixin_Vanilla", + "tags.TagLoaderMixin_Vanilla", + "world.entity.EntityMixin_Vanilla", + "world.entity.EntityTypesMixin_Vanilla", + "world.entity.LivingEntityMixin_Vanilla", + "world.entity.LivingEntityMixin_Vanilla_Damage", + "world.entity.ai.attributes.DefaultAttributesMixin", + "world.entity.animal.golem.SnowGolemMixin_Vanilla", + "world.entity.item.ItemEntityMixin_Vanilla", + "world.entity.player.PlayerMixin_Vanilla_Damage", + "world.entity.vehicle.boat.AbstractBoatMixin_Vanilla", + "world.level.ServerExplosionMixin_Vanilla", + "world.item.TeleportRandomlyConsumeEffectMixin_Vanilla", + "world.level.block.FireBlockMixin_Vanilla", + "world.level.block.entity.AbstractFurnaceBlockEntityMixin_Vanilla", + "world.level.storage.LevelStorageSourceMixin_Vanilla" + ], + "overwrites": { + "conformVisibility": true + } } \ No newline at end of file diff --git a/vanilla/src/mixins/resources/mixins.spongevanilla.core.shared.json b/vanilla/src/mixins/resources/mixins.spongevanilla.core.shared.json index b322779182e..00b0a290898 100644 --- a/vanilla/src/mixins/resources/mixins.spongevanilla.core.shared.json +++ b/vanilla/src/mixins/resources/mixins.spongevanilla.core.shared.json @@ -4,10 +4,10 @@ "package": "org.spongepowered.common.mixin.core", "priority": 1301, "mixins": [ - "server.level.ServerEntityMixin_Shared", - "world.entity.LivingEntityMixin_Shared_Attack_Impl", - "world.entity.player.PlayerMixin_Shared_Attack_Impl", - "world.entity.projectile.FishingHookMixin_Shared" + "world.entity.LivingEntityMixin_Shared_Damage", + "world.entity.player.PlayerMixin_Shared_Damage", + "world.entity.projectile.FishingHookMixin_Shared", + "world.item.crafting.RecipeManagerMixin_Shared" ], "overwrites": { "conformVisibility": true diff --git a/vanilla/src/mixins/resources/mixins.spongevanilla.inventory.json b/vanilla/src/mixins/resources/mixins.spongevanilla.inventory.json index 42d56a58e54..fb0d17bc139 100644 --- a/vanilla/src/mixins/resources/mixins.spongevanilla.inventory.json +++ b/vanilla/src/mixins/resources/mixins.spongevanilla.inventory.json @@ -4,7 +4,6 @@ "package": "org.spongepowered.vanilla.mixin.inventory", "priority": 1200, "mixins": [ - "event.world.level.block.entity.HopperBlockEntityMixin_Inventory_Vanilla" ], "overwrites": { "conformVisibility": true

void sendNormalPacketTo(final EngineConnection connec final ResourceKey key = this.key(); mcPacketSupplier = () -> PacketUtil.createLoginPayloadRequest(new CustomQueryPayload() { @Override - public ResourceLocation id() { - return (ResourceLocation) (Object) key; + public Identifier id() { + return (Identifier) (Object) key; } @Override diff --git a/src/main/java/org/spongepowered/common/network/channel/raw/SpongeRawLoginDataChannel.java b/src/main/java/org/spongepowered/common/network/channel/raw/SpongeRawLoginDataChannel.java index 3a83d426443..35f88ea9784 100644 --- a/src/main/java/org/spongepowered/common/network/channel/raw/SpongeRawLoginDataChannel.java +++ b/src/main/java/org/spongepowered/common/network/channel/raw/SpongeRawLoginDataChannel.java @@ -27,7 +27,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.login.custom.CustomQueryPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.network.EngineConnection; import org.spongepowered.api.network.EngineConnectionState; @@ -170,8 +170,8 @@ public CompletableFuture sendTo(final EngineConnection connection, f final ResourceKey id = this.parent.key(); final Packet mcPacket = PacketUtil.createLoginPayloadRequest(new CustomQueryPayload() { @Override - public ResourceLocation id() { - return (ResourceLocation) (Object) id; + public Identifier id() { + return (Identifier) (Object) id; } @Override diff --git a/src/main/java/org/spongepowered/common/network/packet/ChangeViewerEnvironmentPacket.java b/src/main/java/org/spongepowered/common/network/packet/ChangeViewerEnvironmentPacket.java index 937a61543aa..b1d559c7cec 100644 --- a/src/main/java/org/spongepowered/common/network/packet/ChangeViewerEnvironmentPacket.java +++ b/src/main/java/org/spongepowered/common/network/packet/ChangeViewerEnvironmentPacket.java @@ -25,7 +25,7 @@ package org.spongepowered.common.network.packet; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.dimension.DimensionType; import org.spongepowered.api.network.channel.ChannelBuf; import org.spongepowered.api.network.channel.packet.Packet; @@ -33,7 +33,7 @@ public final class ChangeViewerEnvironmentPacket implements Packet { - public ResourceLocation dimensionLogic; + public Identifier dimensionLogic; public ChangeViewerEnvironmentPacket() { } @@ -44,7 +44,7 @@ public ChangeViewerEnvironmentPacket(final DimensionType dimensionType) { @Override public void read(final ChannelBuf buf) { - this.dimensionLogic = ResourceLocation.parse(buf.readString()); + this.dimensionLogic = Identifier.parse(buf.readString()); } @Override diff --git a/src/main/java/org/spongepowered/common/network/packet/SpongePacketHandler.java b/src/main/java/org/spongepowered/common/network/packet/SpongePacketHandler.java index a77808af907..951e17140a2 100644 --- a/src/main/java/org/spongepowered/common/network/packet/SpongePacketHandler.java +++ b/src/main/java/org/spongepowered/common/network/packet/SpongePacketHandler.java @@ -24,21 +24,18 @@ */ package org.spongepowered.common.network.packet; -import com.mojang.authlib.GameProfile; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.DimensionSpecialEffects; import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.Registries; +import net.minecraft.core.SectionPos; +import net.minecraft.server.players.NameAndId; import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.dimension.DimensionType; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.network.ClientConnectionState; import org.spongepowered.api.network.EngineConnectionStates; import org.spongepowered.api.network.channel.packet.PacketChannel; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.accessor.client.multiplayer.ClientLevelAccessor; import org.spongepowered.common.bridge.CreatorTrackedBridge; import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge; import org.spongepowered.common.network.channel.SpongeChannelManager; @@ -63,7 +60,7 @@ public static void init(final SpongeChannelManager registry) { final net.minecraft.server.level.ServerPlayer sender = (net.minecraft.server.level.ServerPlayer) player; final BlockPos pos = new BlockPos(requestPacket.x, requestPacket.y, requestPacket.z); - if (!sender.level().hasChunkAt(pos)) { + if (!sender.level().getChunkSource().hasChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()))) { return; } @@ -98,9 +95,6 @@ public static void init(final SpongeChannelManager registry) { if (world == null) { return; } - - final DimensionType dimensionType = SpongeCommon.vanillaRegistry(Registries.DIMENSION_TYPE).get(packet.dimensionLogic); - ((ClientLevelAccessor) world).accessor$effects(DimensionSpecialEffects.forType(dimensionType)); } ); } @@ -109,11 +103,11 @@ private static TrackerDataResponsePacket createTrackerDataResponse( final Optional owner, final Optional notifier ) { - final String ownerName = owner.flatMap(x -> SpongeCommon.server().getProfileCache().get(x)) - .map(GameProfile::getName) + final String ownerName = owner.flatMap(SpongeCommon.server().services().nameToIdCache()::get) + .map(NameAndId::name) .orElse(""); - final String notifierName = notifier.flatMap(x -> SpongeCommon.server().getProfileCache().get(x)) - .map(GameProfile::getName) + final String notifierName = notifier.flatMap(SpongeCommon.server().services().nameToIdCache()::get) + .map(NameAndId::name) .orElse(""); return new TrackerDataResponsePacket(ownerName, notifierName); } diff --git a/src/main/java/org/spongepowered/common/network/status/SpongeStatusResponse.java b/src/main/java/org/spongepowered/common/network/status/SpongeStatusResponse.java index b3f4f3e2497..f3c58d58b9d 100644 --- a/src/main/java/org/spongepowered/common/network/status/SpongeStatusResponse.java +++ b/src/main/java/org/spongepowered/common/network/status/SpongeStatusResponse.java @@ -121,7 +121,7 @@ public ServerStatus.Players toVanilla() { // Make sure profiles are sent with non-null UUIDs and names because everything else // will make the response invalid on the client. Some plugins use empty UUIDs to create // custom lines in the player list that do not refer to a specific player. - .map(SpongeGameProfile::toMcProfile) + .map(SpongeGameProfile::toNameAndId) .toList()); } } diff --git a/src/main/java/org/spongepowered/common/profile/SpongeGameProfile.java b/src/main/java/org/spongepowered/common/profile/SpongeGameProfile.java index 1671fe3d08c..0c093c82fbf 100644 --- a/src/main/java/org/spongepowered/common/profile/SpongeGameProfile.java +++ b/src/main/java/org/spongepowered/common/profile/SpongeGameProfile.java @@ -25,8 +25,12 @@ package org.spongepowered.common.profile; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultimap; import com.google.gson.Gson; import com.google.gson.JsonObject; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.minecraft.server.players.NameAndId; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.persistence.DataContainer; @@ -54,21 +58,36 @@ public final class SpongeGameProfile implements GameProfile { private static final Gson GSON = new Gson(); public static final UUID EMPTY_UUID = new UUID(0, 0); + public static SpongeGameProfile of(NameAndId mcProfile) { + final UUID uniqueId = mcProfile.id(); + final var name = mcProfile.name().isEmpty() ? null : mcProfile.name(); + return new SpongeGameProfile(uniqueId, name); + } + public static SpongeGameProfile of(final com.mojang.authlib.GameProfile mcProfile) { - final UUID uniqueId = mcProfile.getId(); - final String name = mcProfile.getName().isEmpty() ? null : mcProfile.getName(); - final List properties = mcProfile.getProperties().values().stream() + final UUID uniqueId = mcProfile.id(); + final String name = mcProfile.name().isEmpty() ? null : mcProfile.name(); + final List properties = mcProfile.properties().values().stream() .map(SpongeProfileProperty::new) .collect(Collectors.toList()); return new SpongeGameProfile(uniqueId, name, properties); } + public static SpongeGameProfile basicOf(final NameAndId mcProfile) { + final var name = mcProfile.name().isEmpty() ? null : mcProfile.name(); + return new SpongeGameProfile(mcProfile.id(), name); + } + public static SpongeGameProfile basicOf(final com.mojang.authlib.GameProfile mcProfile) { - final UUID uniqueId = mcProfile.getId(); - final String name = mcProfile.getName().isEmpty() ? null : mcProfile.getName(); + final UUID uniqueId = mcProfile.id(); + final String name = mcProfile.name().isEmpty() ? null : mcProfile.name(); return new SpongeGameProfile(uniqueId, name); } + public static NameAndId toNameAndId(final GameProfile profile) { + return new NameAndId(profile.uuid(), profile.name().orElse("")); + } + public static com.mojang.authlib.GameProfile toMcProfile(final GameProfile profile) { return ((SpongeGameProfile) profile).toMcProfile(); } @@ -132,11 +151,12 @@ public SpongeGameProfile(final UUID uniqueId, final @Nullable String name, final public com.mojang.authlib.GameProfile toMcProfile() { final UUID uniqueId = this.uniqueId; final String name = (this.name == null) ? "" : this.name; - final com.mojang.authlib.GameProfile mcProfile = new com.mojang.authlib.GameProfile(uniqueId, name); + final ImmutableMultimap.Builder propertyBuilder = ImmutableMultimap.builder(); for (final SpongeProfileProperty property : this.properties) { - mcProfile.getProperties().put(property.name(), property.asProperty()); + propertyBuilder.put(property.name(), property.asProperty()); } - return mcProfile; + final PropertyMap propertyMap = new PropertyMap(propertyBuilder.build()); + return new com.mojang.authlib.GameProfile(uniqueId, name, propertyMap); } @Override diff --git a/src/main/java/org/spongepowered/common/profile/SpongeGameProfileManager.java b/src/main/java/org/spongepowered/common/profile/SpongeGameProfileManager.java index f9ea16c603b..8bd5b7c5846 100644 --- a/src/main/java/org/spongepowered/common/profile/SpongeGameProfileManager.java +++ b/src/main/java/org/spongepowered/common/profile/SpongeGameProfileManager.java @@ -67,7 +67,7 @@ public final class SpongeGameProfileManager implements GameProfileManager { public SpongeGameProfileManager(final Server server) { this.usernameCache = ((SpongeServer) server).getUsernameCache(); - this.cache = (GameProfileCacheBridge) ((MinecraftServer) server).getProfileCache(); + this.cache = (GameProfileCacheBridge) ((MinecraftServer) server).services().nameToIdCache(); this.gameLookupExecutorService = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder() .setNameFormat("Sponge - Async User Lookup Thread").build()); } diff --git a/src/main/java/org/spongepowered/common/profile/UncachedGameProfileProvider.java b/src/main/java/org/spongepowered/common/profile/UncachedGameProfileProvider.java index e25c0215007..31d6c3770b3 100644 --- a/src/main/java/org/spongepowered/common/profile/UncachedGameProfileProvider.java +++ b/src/main/java/org/spongepowered/common/profile/UncachedGameProfileProvider.java @@ -36,6 +36,7 @@ import com.mojang.authlib.ProfileLookupCallback; import com.mojang.authlib.yggdrasil.ProfileResult; import com.mojang.util.UndashedUuid; +import net.minecraft.server.players.NameAndId; import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.core.util.Throwables; import org.checkerframework.checker.nullness.qual.Nullable; @@ -48,6 +49,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; +import java.net.URI; import java.net.URL; import java.time.Duration; import java.time.Instant; @@ -91,7 +93,7 @@ private void submit(final Runnable runnable) { final com.mojang.authlib.GameProfile mcProfile; if (SpongeGameProfileManager.canLookup(uniqueId)) { // only attempt to resolve profiles for online mode clients - ProfileResult result = SpongeCommon.server().getSessionService().fetchProfile(uniqueId, true); + ProfileResult result = SpongeCommon.server().services().sessionService().fetchProfile(uniqueId, true); if (result != null) { mcProfile = result.profile(); } else { @@ -104,7 +106,7 @@ private void submit(final Runnable runnable) { return null; } final CachedProfile cachedProfile = new CachedProfile(SpongeGameProfile.of(mcProfile)); - this.profileByNameCache.put(mcProfile.getName().toLowerCase(Locale.ROOT), cachedProfile); + this.profileByNameCache.put(mcProfile.name().toLowerCase(Locale.ROOT), cachedProfile); return cachedProfile; }); } @@ -184,7 +186,7 @@ public CompletableFuture basicProfile(final String name, final @Nul }); } final CompletableFuture result = new CompletableFuture<>(); - this.submit(() -> SpongeCommon.server().getProfileRepository().findProfilesByNames(new String[]{name}, + this.submit(() -> SpongeCommon.server().services().profileRepository().findProfilesByNames(new String[]{name}, new SingleProfileLookupCallback(result))); return result; } @@ -212,13 +214,13 @@ public CompletableFuture> basicProfiles(final Iterable< } final List nameList = Lists.newArrayList(names); final String[] namesArray = nameList.toArray(new String[0]); - this.submit(() -> SpongeCommon.server().getProfileRepository().findProfilesByNames(namesArray, + this.submit(() -> SpongeCommon.server().services().profileRepository().findProfilesByNames(namesArray, new MapProfileLookupCallback(result, nameList))); return result; } private @Nullable GameProfile requestBasicProfileAt(final String name, final Instant time) throws Exception { - final URL url = new URL("https://api.mojang.com/user/profiles/minecraft/" + name + "?at=" + time.getEpochSecond()); + final URL url = new URI("https://api.mojang.com/user/profiles/minecraft/" + name + "?at=" + time.getEpochSecond()).toURL(); final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.connect(); @@ -287,8 +289,8 @@ private SingleProfileLookupCallback(final CompletableFuture result) } @Override - public void onProfileLookupSucceeded(final com.mojang.authlib.GameProfile profile) { - this.result.complete(SpongeGameProfile.of(profile)); + public void onProfileLookupSucceeded(String profileName, UUID profileId) { + this.result.complete(SpongeGameProfile.of(new NameAndId(profileId, profileName))); } @Override @@ -320,11 +322,11 @@ public void complete() { } @Override - public void onProfileLookupSucceeded(final com.mojang.authlib.GameProfile profile) { + public void onProfileLookupSucceeded(String profileName, UUID profileId) { String originalName = null; while (originalName == null && !this.nameQueue.isEmpty()) { final String name = this.nameQueue.remove(0); - if (name.equalsIgnoreCase(profile.getName())) { + if (name.equalsIgnoreCase(profileName)) { originalName = name; } } @@ -332,7 +334,7 @@ public void onProfileLookupSucceeded(final com.mojang.authlib.GameProfile profil throw new IllegalStateException(); } if (this.resultMap != null) { - this.resultMap.put(originalName, SpongeGameProfile.of(profile)); + this.resultMap.put(originalName, SpongeGameProfile.of(new NameAndId(profileId, profileName))); } } diff --git a/src/main/java/org/spongepowered/common/raid/SpongeRaidWave.java b/src/main/java/org/spongepowered/common/raid/SpongeRaidWave.java index 880c2d79647..4c63019a205 100644 --- a/src/main/java/org/spongepowered/common/raid/SpongeRaidWave.java +++ b/src/main/java/org/spongepowered/common/raid/SpongeRaidWave.java @@ -25,6 +25,7 @@ package org.spongepowered.common.raid; +import net.minecraft.server.level.ServerLevel; import org.spongepowered.api.entity.living.monster.raider.Raider; import org.spongepowered.api.raid.Raid; import org.spongepowered.api.raid.RaidWave; @@ -56,11 +57,7 @@ public boolean isBonus() { * If the wave is not a final wave, the bonus waves occur after the final wave. * If our wave was before the final wave, the amount of normal raids (which is set by the difficulty) would be greater than our wave's id. */ - if (this.isFinal() || this.waveId < ((RaidAccessor) this.raid).accessor$numGroups()) { - return false; - } - - return true; + return !this.isFinal() && this.waveId >= ((RaidAccessor) this.raid).accessor$numGroups(); } @Override @@ -78,14 +75,18 @@ public Optional leader() { @Override public boolean addRaider(Raider raider, boolean addToRaidHealth) { Objects.requireNonNull(raider, "Raider cannot be null."); - return this.raid.addWaveMob(this.waveId, (net.minecraft.world.entity.raid.Raider) raider, addToRaidHealth); + // We only support adding raiders to server worlds + if (raider.world() instanceof ServerLevel sl) { + return this.raid.addWaveMob(sl, this.waveId, (net.minecraft.world.entity.raid.Raider) raider, addToRaidHealth); + } + return false; } @Override public boolean removeRaider(Raider raider) { Objects.requireNonNull(raider, "Raider cannot be null."); - if (raider.raidWave().isPresent() && this.equals(raider.raidWave().get().get())) { - this.raid.removeFromRaid((net.minecraft.world.entity.raid.Raider) raider, true); + if (raider.raidWave().isPresent() && this.equals(raider.raidWave().get().get()) && raider.world() instanceof ServerLevel sl) { + this.raid.removeFromRaid(sl, (net.minecraft.world.entity.raid.Raider) raider, true); return true; } @@ -94,11 +95,10 @@ public boolean removeRaider(Raider raider) { @Override public boolean equals(Object obj) { - if (obj instanceof SpongeRaidWave) { - SpongeRaidWave other = (SpongeRaidWave) obj; + if (obj instanceof SpongeRaidWave other) { // Minecraft Tracks it's raids via an ID which is handled by the RaidManager. // Each world has it's own raid manager so we have to verify that the world the raids are in is also equal. - if (this.waveId == other.waveId && this.raid.getLevel() == other.raid.getLevel() && this.raid.getId() == other.raid.getId()) { + if (this.waveId == other.waveId && this.raid == other.raid && this.raid.getCenter() == other.raid.getCenter()) { return true; } } diff --git a/src/main/java/org/spongepowered/common/registry/InitialRegistryData.java b/src/main/java/org/spongepowered/common/registry/InitialRegistryData.java index 2774ffb923a..9f8bbe1a04d 100644 --- a/src/main/java/org/spongepowered/common/registry/InitialRegistryData.java +++ b/src/main/java/org/spongepowered/common/registry/InitialRegistryData.java @@ -36,7 +36,7 @@ public abstract class InitialRegistryData { abstract @Nullable Map keyToId(); - void forEach(final LoaderConsumer consumer) { + public void forEach(final LoaderConsumer consumer) { final Map kv = this.keyToValue(); final Map ki = this.keyToId(); @@ -56,7 +56,7 @@ void forEach(final LoaderConsumer consumer) { } } - static @Nullable InitialRegistryData noIds(final @Nullable Supplier> values) { + public static @Nullable InitialRegistryData noIds(final @Nullable Supplier> values) { if (values == null) { return null; } @@ -73,7 +73,7 @@ Map keyToValue() { }; } - interface LoaderConsumer { + public interface LoaderConsumer { void accept(final ResourceKey key, final OptionalInt id, final T value); } } diff --git a/src/main/java/org/spongepowered/common/registry/ProxiedRegistryAccess.java b/src/main/java/org/spongepowered/common/registry/ProxiedRegistryAccess.java deleted file mode 100644 index f37c2ef7ac5..00000000000 --- a/src/main/java/org/spongepowered/common/registry/ProxiedRegistryAccess.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.registry; - -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceKey; -import org.checkerframework.checker.nullness.qual.Nullable; - -import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; - -public final class ProxiedRegistryAccess implements RegistryAccess { - - private final RegistryAccess access; - private final Map>, Registry> overrides; - - public ProxiedRegistryAccess(final RegistryAccess access, final Map>, Registry> overrides) { - this.access = access; - this.overrides = Map.copyOf(overrides); - } - - @Override - @SuppressWarnings("unchecked") - public Optional> registry(final ResourceKey> var1) { - final @Nullable Registry override = this.overrides.get(var1); - if (override != null) { - return Optional.of((Registry) override); - } - return this.access.registry(var1); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public Stream> registries() { - return Stream.concat( - this.access.registries().filter(entry -> !this.overrides.containsKey(entry.key())), - (Stream>) (Stream) this.overrides.entrySet().stream().map(entry -> new RegistryAccess.RegistryEntry(entry.getKey(), entry.getValue())) - ); - } - -} diff --git a/src/main/java/org/spongepowered/common/registry/RegistryHolderLogic.java b/src/main/java/org/spongepowered/common/registry/RegistryHolderLogic.java index ee5b05dd2c7..d6838c10da4 100644 --- a/src/main/java/org/spongepowered/common/registry/RegistryHolderLogic.java +++ b/src/main/java/org/spongepowered/common/registry/RegistryHolderLogic.java @@ -26,12 +26,16 @@ import com.mojang.serialization.Lifecycle; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.core.HolderLookup; import net.minecraft.core.MappedRegistry; import net.minecraft.core.RegistrationInfo; import net.minecraft.core.RegistryAccess; import net.minecraft.core.WritableRegistry; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; +import net.minecraft.util.DependencySorter; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.flag.FeatureFlags; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.registry.DuplicateRegistrationException; @@ -42,7 +46,6 @@ import org.spongepowered.api.registry.ValueNotFoundException; import org.spongepowered.common.accessor.core.MappedRegistryAccessor; import org.spongepowered.common.accessor.resources.ResourceKeyAccessor; -import org.spongepowered.common.bridge.core.MappedRegistryBridge; import org.spongepowered.common.bridge.core.WritableRegistryBridge; import java.util.Map; @@ -53,19 +56,21 @@ import java.util.function.Supplier; import java.util.stream.Stream; -public final class RegistryHolderLogic implements RegistryHolder { +public final class RegistryHolderLogic implements RegistryHolder, HolderLookup.Provider { private final Map>> roots = new Object2ObjectOpenHashMap<>(); + private @Nullable FeatureFlagSet featureFlagSet; + public RegistryHolderLogic() { this.roots.put( - (ResourceKey) (Object) ResourceLocation.withDefaultNamespace("root"), + RegistryRoots.MINECRAFT, new MappedRegistry<>( - net.minecraft.resources.ResourceKey.createRegistryKey((ResourceLocation) (Object) RegistryRoots.MINECRAFT), + net.minecraft.resources.ResourceKey.createRegistryKey((Identifier) (Object) RegistryRoots.MINECRAFT), Lifecycle.experimental() ) ); - final ResourceLocation sponge = ResourceLocation.fromNamespaceAndPath("sponge", "root"); + final Identifier sponge = (Identifier) (Object) RegistryRoots.SPONGE; this.roots.put( (ResourceKey) (Object) sponge, new MappedRegistry<>( @@ -81,37 +86,59 @@ public RegistryHolderLogic() { public RegistryHolderLogic(final RegistryAccess dynamicAccess) { this(); - final WritableRegistry root = (WritableRegistry) this.roots.get(ResourceLocation.withDefaultNamespace("root")); + final WritableRegistry root = (WritableRegistry) this.roots.get(RegistryRoots.MINECRAFT); // Add the dynamic registries. These are server-scoped in Vanilla dynamicAccess.registries().forEach(entry -> root.register(entry.key(), entry.value(), RegistrationInfo.BUILT_IN)); root.freeze(); } + public RegistryHolderLogic(final RegistryAccess dynamicAccess, final @Nullable FeatureFlagSet featureFlagSet) { + this(dynamicAccess); + + this.featureFlagSet = featureFlagSet; + } + public void setRootMinecraftRegistry(final net.minecraft.core.Registry> rootRegistry) { this.roots.put(RegistryRoots.MINECRAFT, rootRegistry); } + @SuppressWarnings({"unchecked", "rawtypes"}) + public void setRootMinecraftRegistry(final RegistryAccess registryAccess) { + final MappedRegistry rootRegistry = new MappedRegistry<>( + net.minecraft.resources.ResourceKey.createRegistryKey((Identifier) (Object) RegistryRoots.MINECRAFT), + Lifecycle.experimental() + ); + registryAccess.registries().forEach(r -> rootRegistry.register(r.key(), r.value(), RegistrationInfo.BUILT_IN)); + rootRegistry.freeze(); + this.roots.put(RegistryRoots.MINECRAFT, rootRegistry); + } + + @SuppressWarnings("unchecked") @Override public Registry registry(final RegistryType type) { - final net.minecraft.core.Registry> root = this.roots.get(Objects.requireNonNull(type, "type").root()); + final var root = (Registry>) this.roots.get(Objects.requireNonNull(type, "type").root()); if (root == null) { throw new ValueNotFoundException(String.format("No '%s' root registry has been defined", type.root())); } - final net.minecraft.core.Registry registry = root.get((ResourceLocation) (Object) type.location()); - if (registry == null) { - throw new ValueNotFoundException(String.format("No '%s' registry has been defined in root '%s'", type.location(), type.root())); - } + final var registry = root.findValue(type.location()) + .orElseThrow(() -> new ValueNotFoundException(String.format("No '%s' registry has been defined in root '%s'", type.location(), type.root()))); return (Registry) registry; } + @SuppressWarnings("unchecked") @Override public Optional> findRegistry(final RegistryType type) { - final net.minecraft.core.Registry> root = this.roots.get(Objects.requireNonNull(type, "type").root()); + final Registry> root = (Registry>) this.roots.get(Objects.requireNonNull(type, "type").root()); if (root == null) { return Optional.empty(); } - return (Optional>) (Object) root.getOptional((ResourceLocation) (Object) type.location()); + return root.findValue(type.location()); + } + + @Override + public Stream> streamRegistries() { + return this.roots.values().stream().flatMap(r -> (Stream>) (Object) r.stream()); } @Override @@ -133,7 +160,7 @@ public Registry createRegistry(final RegistryType type, final @Nullabl public Registry createRegistry(final RegistryType type, final @Nullable Supplier> defaultValues, final boolean isDynamic) { - return this.createRegistry(type, InitialRegistryData.noIds(defaultValues), this.registrySupplier(isDynamic, null), false); + return this.createRegistry(type, InitialRegistryData.noIds(defaultValues), this.registrySupplier(isDynamic, null)); } @SuppressWarnings("unchecked") @@ -143,36 +170,38 @@ public Function { final MappedRegistry reg = new MappedRegistry<>(key, Lifecycle.stable()); - ((WritableRegistryBridge)reg).bridge$setDynamic(isDynamic); + ((WritableRegistryBridge) reg).bridge$setDynamic(isDynamic); + ((WritableRegistryBridge) reg).bridge$setRegistryHolder(this); return reg; }; } else { return (key) -> { final CallbackRegistry reg = new CallbackRegistry<>(key, Lifecycle.stable(), callback); ((WritableRegistryBridge) (Object) reg).bridge$setDynamic(isDynamic); + ((WritableRegistryBridge) (Object) reg).bridge$setRegistryHolder(this); return reg; }; } } public Registry createRegistry(final RegistryType type, final @Nullable InitialRegistryData defaultValues, - final Function>, net.minecraft.core.Registry> registrySupplier, final boolean replace) { + final Function>, net.minecraft.core.Registry> registrySupplier) { final net.minecraft.core.Registry> root = this.roots.get(Objects.requireNonNull(type, "type").root()); if (root == null) { throw new ValueNotFoundException(String.format("No '%s' root registry has been defined", type.root())); } - net.minecraft.core.Registry registry = root.get((ResourceLocation) (Object) type.location()); + var registry = root.getValue((Identifier) (Object) type.location()); final boolean exists = registry != null; - if (!replace && exists) { + if (exists) { throw new DuplicateRegistrationException(String.format("Registry '%s' in root '%s' has already been defined", type.location(), type.root())); } final net.minecraft.resources.ResourceKey> key; if (Registries.ROOT_REGISTRY_NAME.equals(type.root())) { - key = net.minecraft.resources.ResourceKey.createRegistryKey((ResourceLocation) (Object) type.location()); + key = net.minecraft.resources.ResourceKey.createRegistryKey((Identifier) (Object) type.location()); } else { key = ResourceKeyAccessor.invoker$create( - (ResourceLocation) (Object) RegistryRoots.SPONGE, - (ResourceLocation) (Object) type.location() + (Identifier) (Object) RegistryRoots.SPONGE, + (Identifier) (Object) type.location() ); } registry = registrySupplier.apply(key); @@ -181,7 +210,7 @@ public Registry createRegistry(final RegistryType type, final @Nullabl final MappedRegistry mr = (MappedRegistry) registry; defaultValues.forEach((vk, vi, vv) -> { mr.register( - net.minecraft.resources.ResourceKey.create(key, (ResourceLocation) (Object) vk), + net.minecraft.resources.ResourceKey.create(key, (Identifier) (Object) vk), vv, RegistrationInfo.BUILT_IN ); @@ -193,19 +222,10 @@ public Registry createRegistry(final RegistryType type, final @Nullabl }); } - // This is so wrong and dirty and only because we don't have layered registries... - final boolean frozen = ((MappedRegistryAccessor>) root).accessor$frozen(); - - if (replace && exists) { - ((MappedRegistryAccessor>) root).accessor$frozen(false); - ((MappedRegistryBridge>) root).bridge$forceRemoveValue(key); - } - ((WritableRegistry) root).register(key, registry, RegistrationInfo.BUILT_IN); if (registry instanceof CallbackRegistry) { ((CallbackRegistry) registry).setCallbackEnabled(true); } - ((MappedRegistryAccessor>) root).accessor$frozen(frozen); return (Registry) registry; } @@ -214,7 +234,35 @@ public void freezeSpongeRootRegistry() { this.roots.get(RegistryRoots.SPONGE).freeze(); } - public void freezeSpongeDynamicRegistries() { - this.roots.get(RegistryRoots.SPONGE).forEach(net.minecraft.core.Registry::freeze); + public void freezeSpongeDynamicRegistries(final boolean force) { + final DependencySorter, SpongeRegistryDependencyEntry>> dependencies = new DependencySorter<>(); + final net.minecraft.core.Registry> registry = this.roots.get(RegistryRoots.SPONGE); + registry.stream() + .filter(r -> force || (((WritableRegistryBridge) r).bridge$eventCalled() && ((WritableRegistryBridge) r).bridge$pendingDependencies() + .allMatch(t -> this.findRegistry(t).map(v -> ((MappedRegistryAccessor) v).accessor$frozen() && ((WritableRegistryBridge) v).bridge$eventCalled()).orElse(false)))) + .forEach(r -> dependencies.addEntry( + ((Registry) r).type(), new SpongeRegistryDependencyEntry<>(r, ((WritableRegistryBridge) r).bridge$pendingDependencies().toList()))); + dependencies.orderByDependencies(($, v) -> v.cookie().freeze()); + } + + public FeatureFlagSet featureFlagSet() { + return this.featureFlagSet == null ? FeatureFlags.VANILLA_SET : this.featureFlagSet; + } + + public void featureFlagSet(final FeatureFlagSet featureFlagSet) { + this.featureFlagSet = featureFlagSet; + } + + @Override + public Stream>> listRegistryKeys() { + return this.streamRegistries().map(k -> + ResourceKeyAccessor.invoker$create((Identifier) (Object) k.type().root(), (Identifier) (Object) k.type().location())); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Override + public Optional> lookup( + final net.minecraft.resources.ResourceKey> resourceKey) { + return (Optional) this.findRegistry(RegistryType.of((ResourceKey) (Object) resourceKey.registry(), (ResourceKey) (Object) resourceKey.identifier())); } } diff --git a/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java b/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java index 726bb27bf8b..3ddcd3873f3 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java @@ -27,13 +27,13 @@ import com.google.inject.Singleton; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.advancement.AdvancementTemplate; +import org.spongepowered.api.advancement.Advancement; import org.spongepowered.api.advancement.DisplayInfo; import org.spongepowered.api.advancement.criteria.AdvancementCriterion; import org.spongepowered.api.advancement.criteria.ScoreAdvancementCriterion; import org.spongepowered.api.advancement.criteria.trigger.FilteredTrigger; import org.spongepowered.api.advancement.criteria.trigger.Trigger; -import org.spongepowered.api.adventure.ChatTypeTemplate; +import org.spongepowered.api.adventure.ChatType; import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.entity.BlockEntityArchetype; @@ -50,7 +50,9 @@ import org.spongepowered.api.data.MutableDataProviderBuilder; import org.spongepowered.api.data.meta.BannerPatternLayer; import org.spongepowered.api.data.persistence.DataStore; -import org.spongepowered.api.data.type.ArtTypeTemplate; +import org.spongepowered.api.data.type.ArtType; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; import org.spongepowered.api.effect.particle.ParticleEffect; import org.spongepowered.api.effect.potion.PotionEffect; import org.spongepowered.api.effect.sound.SoundType; @@ -68,7 +70,8 @@ import org.spongepowered.api.entity.attribute.AttributeModifier; import org.spongepowered.api.entity.living.player.tab.TabListEntry; import org.spongepowered.api.event.EventContextKey; -import org.spongepowered.api.event.cause.entity.damage.DamageTypeTemplate; +import org.spongepowered.api.event.cause.entity.damage.DamageModifier; +import org.spongepowered.api.event.cause.entity.damage.DamageType; import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; import org.spongepowered.api.fluid.FluidStack; import org.spongepowered.api.fluid.FluidStackSnapshot; @@ -107,34 +110,33 @@ import org.spongepowered.api.util.ResettableBuilder; import org.spongepowered.api.util.transformation.Transformation; import org.spongepowered.api.world.LocatableBlock; +import org.spongepowered.api.world.WorldType; import org.spongepowered.api.world.WorldTypeEffect; -import org.spongepowered.api.world.WorldTypeTemplate; -import org.spongepowered.api.world.biome.BiomeTemplate; +import org.spongepowered.api.world.biome.Biome; import org.spongepowered.api.world.biome.provider.CheckerboardBiomeConfig; import org.spongepowered.api.world.biome.provider.EndStyleBiomeConfig; import org.spongepowered.api.world.biome.provider.MultiNoiseBiomeConfig; import org.spongepowered.api.world.border.WorldBorder; import org.spongepowered.api.world.explosion.Explosion; -import org.spongepowered.api.world.generation.carver.CarverTemplate; +import org.spongepowered.api.world.generation.carver.Carver; import org.spongepowered.api.world.generation.config.WorldGenerationConfig; import org.spongepowered.api.world.generation.config.flat.FlatGeneratorConfig; -import org.spongepowered.api.world.generation.config.noise.DensityFunctionTemplate; +import org.spongepowered.api.world.generation.config.noise.Noise; import org.spongepowered.api.world.generation.config.noise.NoiseConfig; -import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfigTemplate; -import org.spongepowered.api.world.generation.config.noise.NoiseTemplate; -import org.spongepowered.api.world.generation.feature.FeatureTemplate; -import org.spongepowered.api.world.generation.feature.PlacedFeatureTemplate; -import org.spongepowered.api.world.generation.structure.SchematicTemplate; +import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfig; +import org.spongepowered.api.world.generation.feature.Feature; +import org.spongepowered.api.world.generation.feature.PlacedFeature; import org.spongepowered.api.world.generation.structure.StructurePlacement; -import org.spongepowered.api.world.generation.structure.StructureSetTemplate; -import org.spongepowered.api.world.generation.structure.StructureTemplate; -import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPoolTemplate; -import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorListTemplate; +import org.spongepowered.api.world.generation.structure.StructureSet; +import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPool; +import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorList; import org.spongepowered.api.world.portal.PortalLogic; import org.spongepowered.api.world.schematic.PaletteType; import org.spongepowered.api.world.schematic.Schematic; import org.spongepowered.api.world.server.TicketType; -import org.spongepowered.api.world.server.WorldTemplate; +import org.spongepowered.api.world.server.WorldArchetype; +import org.spongepowered.api.world.server.WorldArchetypeType; +import org.spongepowered.api.world.server.storage.ServerWorldProperties; import org.spongepowered.api.world.volume.stream.StreamOptions; import org.spongepowered.common.advancement.SpongeAdvancementBuilder; import org.spongepowered.common.advancement.SpongeDisplayInfoBuilder; @@ -142,7 +144,7 @@ import org.spongepowered.common.advancement.SpongeTriggerBuilder; import org.spongepowered.common.advancement.criterion.SpongeCriterionBuilder; import org.spongepowered.common.advancement.criterion.SpongeScoreCriterionBuilder; -import org.spongepowered.common.adventure.SpongeChatTypeTemplate; +import org.spongepowered.common.adventure.SpongeChatTypeBuilder; import org.spongepowered.common.ban.SpongeBanBuilder; import org.spongepowered.common.block.SpongeBlockSnapshot; import org.spongepowered.common.block.SpongeBlockStateBuilder; @@ -162,7 +164,7 @@ import org.spongepowered.common.data.key.SpongeKeyBuilder; import org.spongepowered.common.data.persistence.datastore.SpongeDataStoreBuilder; import org.spongepowered.common.data.provider.DataProviderRegistrator; -import org.spongepowered.common.data.type.SpongeArtTypeTemplate; +import org.spongepowered.common.data.type.SpongeArtTypeBuilder; import org.spongepowered.common.effect.particle.SpongeParticleEffectBuilder; import org.spongepowered.common.effect.potion.SpongePotionBuilder; import org.spongepowered.common.effect.sound.SpongeSoundBuilder; @@ -180,8 +182,9 @@ import org.spongepowered.common.entity.attribute.SpongeAttributeModifierBuilder; import org.spongepowered.common.entity.player.tab.TabListEntryBuilder; import org.spongepowered.common.event.SpongeEventContextKeyBuilder; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageModifier; import org.spongepowered.common.event.cause.entity.damage.SpongeDamageSourceBuilder; -import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTypeTemplate; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTypeBuilder; import org.spongepowered.common.fluid.SpongeFluidStackBuilder; import org.spongepowered.common.fluid.SpongeFluidStackSnapshotBuilder; import org.spongepowered.common.fluid.SpongeFluidStateBuilder; @@ -203,6 +206,8 @@ import org.spongepowered.common.item.recipe.ingredient.SpongeIngredientBuilder; import org.spongepowered.common.item.recipe.smithing.SpongeSmithingRecipeBuilder; import org.spongepowered.common.item.recipe.stonecutting.SpongeStoneCutterRecipeBuilder; +import org.spongepowered.common.item.shield.SpongeShieldDamageReductionMultiplyAddBuilder; +import org.spongepowered.common.item.shield.SpongeShieldItemDamageFunctionMultiplyAddBuilder; import org.spongepowered.common.map.canvas.SpongeMapCanvasBuilder; import org.spongepowered.common.map.color.SpongeMapColorBuilder; import org.spongepowered.common.map.decoration.SpongeMapDecorationBuilder; @@ -216,33 +221,32 @@ import org.spongepowered.common.util.transformation.SpongeTransformationBuilder; import org.spongepowered.common.world.SpongeExplosionBuilder; import org.spongepowered.common.world.SpongeWorldTypeEffect; -import org.spongepowered.common.world.biome.SpongeBiomeTemplate; +import org.spongepowered.common.world.biome.SpongeBiomeBuilder; import org.spongepowered.common.world.biome.provider.SpongeCheckerboardBiomeConfig; import org.spongepowered.common.world.biome.provider.SpongeEndStyleBiomeConfig; import org.spongepowered.common.world.biome.provider.SpongeMultiNoiseBiomeConfig; import org.spongepowered.common.world.border.SpongeWorldBorderBuilder; import org.spongepowered.common.world.generation.SpongeWorldGenerationConfigBuilder; -import org.spongepowered.common.world.generation.carver.SpongeCarverTemplate; +import org.spongepowered.common.world.generation.carver.SpongeCarverBuilder; import org.spongepowered.common.world.generation.config.flat.SpongeFlatGeneratorConfig; -import org.spongepowered.common.world.generation.config.noise.SpongeDensityFunctionTemplate; +import org.spongepowered.common.world.generation.config.noise.SpongeNoiseBuilder; import org.spongepowered.common.world.generation.config.noise.SpongeNoiseConfig; -import org.spongepowered.common.world.generation.config.noise.SpongeNoiseGeneratorConfigTemplate; -import org.spongepowered.common.world.generation.config.noise.SpongeNoiseTemplate; -import org.spongepowered.common.world.generation.feature.SpongeFeatureTemplate; -import org.spongepowered.common.world.generation.feature.SpongePlacedFeatureTemplate; -import org.spongepowered.common.world.generation.structure.SpongeSchematicTemplate; +import org.spongepowered.common.world.generation.config.noise.SpongeNoiseGeneratorConfigBuilder; +import org.spongepowered.common.world.generation.feature.SpongeFeatureBuilder; +import org.spongepowered.common.world.generation.feature.SpongePlacedFeatureBuilder; import org.spongepowered.common.world.generation.structure.SpongeStructurePlacementBuilder; -import org.spongepowered.common.world.generation.structure.SpongeStructureSetTemplate; -import org.spongepowered.common.world.generation.structure.SpongeStructureTemplate; -import org.spongepowered.common.world.generation.structure.jigsaw.SpongeJigsawPoolTemplate; -import org.spongepowered.common.world.generation.structure.jigsaw.SpongeProcessorListTemplate; +import org.spongepowered.common.world.generation.structure.SpongeStructureSetBuilder; +import org.spongepowered.common.world.generation.structure.jigsaw.SpongeJigsawPoolBuilder; +import org.spongepowered.common.world.generation.structure.jigsaw.SpongeProcessorListBuilder; import org.spongepowered.common.world.portal.SpongePortalLogicBuilder; import org.spongepowered.common.world.schematic.SpongePaletteTypeBuilder; import org.spongepowered.common.world.schematic.SpongeSchematicBuilder; import org.spongepowered.common.world.server.SpongeLocatableBlockBuilder; +import org.spongepowered.common.world.server.SpongeServerWorldPropertiesLoadOptions; import org.spongepowered.common.world.server.SpongeTicketTypeBuilder; -import org.spongepowered.common.world.server.SpongeWorldTemplate; -import org.spongepowered.common.world.server.SpongeWorldTypeTemplate; +import org.spongepowered.common.world.server.SpongeWorldArchetype; +import org.spongepowered.common.world.server.SpongeWorldArchetypeType; +import org.spongepowered.common.world.server.SpongeWorldTypeBuilder; import org.spongepowered.common.world.volume.stream.SpongeStreamOptionsBuilder; import java.util.Map; @@ -294,6 +298,7 @@ public void registerDefaultBuilders() { .register(Team.Builder.class, SpongeTeamBuilder::new) .register(Scoreboard.Builder.class, SpongeScoreboardBuilder::new) .register(DamageSource.Builder.class, SpongeDamageSourceBuilder::new) + .register(DamageModifier.Builder.class, SpongeDamageModifier.Builder::new) .register(Explosion.Builder.class, SpongeExplosionBuilder::new) .register(BlockState.Builder.class, SpongeBlockStateBuilder::new) .register(BlockSnapshot.Builder.class, SpongeBlockSnapshot.BuilderImpl::unpooled) @@ -337,7 +342,7 @@ public void registerDefaultBuilders() { .register(Enchantment.Builder.class, SpongeEnchantmentBuilder::new) .register(Enchantment.RandomListBuilder.class, SpongeRandomEnchantmentListBuilder::new) .register(Key.Builder.class, SpongeKeyBuilder::new) - .register(AdvancementTemplate.Builder.class, SpongeAdvancementBuilder::new) + .register(Advancement.Builder.class, SpongeAdvancementBuilder::new) .register(DisplayInfo.Builder.class, SpongeDisplayInfoBuilder::new) .register(AdvancementCriterion.Builder.class, SpongeCriterionBuilder::new) .register(ScoreAdvancementCriterion.Builder.class, SpongeScoreCriterionBuilder::new) @@ -366,13 +371,12 @@ public void registerDefaultBuilders() { .register(FluidState.Builder.class, SpongeFluidStateBuilder::new) .register(WorldGenerationConfig.Builder.class, SpongeWorldGenerationConfigBuilder::new) .register(WorldTypeEffect.Builder.class, SpongeWorldTypeEffect.BuilderImpl::new) - .register(WorldTypeTemplate.Builder.class, SpongeWorldTypeTemplate.BuilderImpl::new) - .register(WorldTemplate.Builder.class, SpongeWorldTemplate.BuilderImpl::new) + .register(WorldType.Builder.class, SpongeWorldTypeBuilder::new) + .register(WorldArchetypeType.Builder.class, SpongeWorldArchetypeType::new) .register(NoiseConfig.Builder.class, SpongeNoiseConfig.BuilderImpl::new) .register(FlatGeneratorConfig.Builder.class, SpongeFlatGeneratorConfig.BuilderImpl::new) - .register(NoiseGeneratorConfigTemplate.Builder.class, SpongeNoiseGeneratorConfigTemplate.BuilderImpl::new) - .register(NoiseTemplate.Builder.class, SpongeNoiseTemplate.BuilderImpl::new) - .register(DensityFunctionTemplate.Builder.class, SpongeDensityFunctionTemplate.BuilderImpl::new) + .register(NoiseGeneratorConfig.Builder.class, SpongeNoiseGeneratorConfigBuilder::new) + .register(Noise.Builder.class, SpongeNoiseBuilder::new) .register(CheckerboardBiomeConfig.Builder.class, SpongeCheckerboardBiomeConfig.BuilderImpl::new) .register(EndStyleBiomeConfig.Builder.class, SpongeEndStyleBiomeConfig.BuilderImpl::new) .register(MultiNoiseBiomeConfig.Builder.class, SpongeMultiNoiseBiomeConfig.BuilderImpl::new) @@ -380,21 +384,23 @@ public void registerDefaultBuilders() { .register(MapDecoration.Builder.class, SpongeMapDecorationBuilder::new) .register(MapCanvas.Builder.class, SpongeMapCanvasBuilder::new) .register(Transformation.Builder.class, SpongeTransformationBuilder::new) - .register(BiomeTemplate.Builder.class, SpongeBiomeTemplate.BuilderImpl::new) - .register(CarverTemplate.Builder.class, SpongeCarverTemplate.BuilderImpl::new) - .register(FeatureTemplate.Builder.class, SpongeFeatureTemplate.BuilderImpl::new) - .register(PlacedFeatureTemplate.Builder.class, SpongePlacedFeatureTemplate.BuilderImpl::new) - .register(StructureTemplate.Builder.class, SpongeStructureTemplate.BuilderImpl::new) - .register(SchematicTemplate.Builder.class, SpongeSchematicTemplate.BuilderImpl::new) - .register(ProcessorListTemplate.Builder.class, SpongeProcessorListTemplate.BuilderImpl::new) + .register(Biome.Builder.class, SpongeBiomeBuilder::new) + .register(Carver.Builder.class, SpongeCarverBuilder::new) + .register(Feature.Builder.class, SpongeFeatureBuilder::new) + .register(PlacedFeature.Builder.class, SpongePlacedFeatureBuilder::new) + .register(ProcessorList.Builder.class, SpongeProcessorListBuilder::new) .register(StructurePlacement.Builder.class, SpongeStructurePlacementBuilder::new) - .register(StructureSetTemplate.Builder.class, SpongeStructureSetTemplate.BuilderImpl::new) - .register(JigsawPoolTemplate.Builder.class, SpongeJigsawPoolTemplate.BuilderImpl::new) - .register(ChatTypeTemplate.Builder.class, SpongeChatTypeTemplate.BuilderImpl::new) - .register(DamageTypeTemplate.Builder.class, SpongeDamageTypeTemplate.BuilderImpl::new) - .register(ArtTypeTemplate.Builder.class, SpongeArtTypeTemplate.BuilderImpl::new) + .register(StructureSet.Builder.class, SpongeStructureSetBuilder::new) + .register(JigsawPool.Builder.class, SpongeJigsawPoolBuilder::new) + .register(ChatType.Builder.class, SpongeChatTypeBuilder::new) + .register(DamageType.Builder.class, SpongeDamageTypeBuilder::new) + .register(ArtType.Builder.class, SpongeArtTypeBuilder::new) .register(TicketType.Builder.class, SpongeTicketTypeBuilder::new) .register(PortalLogic.Builder.class, SpongePortalLogicBuilder::new) + .register(ServerWorldProperties.LoadOptions.Builder.class, SpongeServerWorldPropertiesLoadOptions.BuilderImpl::new) + .register(WorldArchetype.Builder.class, SpongeWorldArchetype.BuilderImpl::new) + .register(ShieldDamageReduction.MultiplyAdd.Builder.class, SpongeShieldDamageReductionMultiplyAddBuilder::new) + .register(ShieldItemDamageFunction.MultiplyAdd.Builder.class, SpongeShieldItemDamageFunctionMultiplyAddBuilder::new) ; } } diff --git a/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java b/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java index a6802500de8..a7c58cdbb52 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java @@ -41,12 +41,16 @@ import org.spongepowered.api.command.registrar.tree.CommandTreeNode; import org.spongepowered.api.command.selector.Selector; import org.spongepowered.api.data.DataManipulator; +import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; import org.spongepowered.api.data.type.ToolRule; import org.spongepowered.api.data.value.Value; -import org.spongepowered.api.datapack.DataPackType; import org.spongepowered.api.effect.ForwardingViewer; import org.spongepowered.api.effect.VanishState; +import org.spongepowered.api.entity.attribute.ItemAttribute; import org.spongepowered.api.event.EventListenerRegistration; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.item.inventory.ItemStackComparators; @@ -61,6 +65,7 @@ import org.spongepowered.api.registry.FactoryProvider; import org.spongepowered.api.registry.RegistryKey; import org.spongepowered.api.registry.RegistryReference; +import org.spongepowered.api.registry.RegistryRegistrationSet; import org.spongepowered.api.registry.RegistryType; import org.spongepowered.api.registry.TypeNotFoundException; import org.spongepowered.api.resource.ResourcePath; @@ -74,7 +79,6 @@ import org.spongepowered.api.state.IntegerStateProperty; import org.spongepowered.api.state.StateMatcher; import org.spongepowered.api.tag.Tag; -import org.spongepowered.api.tag.TagTemplate; import org.spongepowered.api.util.AABB; import org.spongepowered.api.util.MinecraftDayTime; import org.spongepowered.api.util.Range; @@ -97,11 +101,11 @@ import org.spongepowered.api.world.generation.config.SurfaceRule; import org.spongepowered.api.world.generation.config.flat.LayerConfig; import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPoolElement; +import org.spongepowered.api.world.generation.structure.jigsaw.Processor; import org.spongepowered.api.world.portal.PortalLogic; import org.spongepowered.api.world.schematic.PaletteReference; import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerLocationCreator; -import org.spongepowered.api.world.server.WorldTemplate; import org.spongepowered.api.world.volume.archetype.entity.EntityArchetypeEntry; import org.spongepowered.api.world.volume.block.BlockVolumeFactory; import org.spongepowered.api.world.weather.Weather; @@ -122,16 +126,20 @@ import org.spongepowered.common.data.manipulator.ImmutableDataManipulatorFactory; import org.spongepowered.common.data.manipulator.MutableDataManipulatorFactory; import org.spongepowered.common.data.value.SpongeValueFactory; -import org.spongepowered.common.datapack.SpongeDataPackType; import org.spongepowered.common.effect.SpongeCustomForwardingViewer; +import org.spongepowered.common.entity.attribute.SpongeItemAttributeFactory; import org.spongepowered.common.entity.effect.SpongeVanishState; import org.spongepowered.common.event.SpongeEventListenerRegistration; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageStepType; import org.spongepowered.common.event.tracking.BlockChangeFlagManager; +import org.spongepowered.common.item.SpongeItemActionFactory; import org.spongepowered.common.item.SpongeItemStack; import org.spongepowered.common.item.SpongeItemStackSnapshot; import org.spongepowered.common.item.SpongeToolRuleFactory; import org.spongepowered.common.item.recipe.SpongeRecipeInputFactory; import org.spongepowered.common.item.recipe.smithing.SpongeArmorTrimFactory; +import org.spongepowered.common.item.shield.SpongeShieldDamageReductionFactory; +import org.spongepowered.common.item.shield.SpongeShieldItemDamageFunctionFactory; import org.spongepowered.common.item.util.SpongeItemStackComparatorFactory; import org.spongepowered.common.network.channel.SpongeChannelExceptionHandlerFactory; import org.spongepowered.common.network.status.SpongeFavicon; @@ -145,7 +153,6 @@ import org.spongepowered.common.service.server.permission.SpongeNodeTree; import org.spongepowered.common.state.SpongeStateMatcherFactory; import org.spongepowered.common.tag.SpongeTagFactory; -import org.spongepowered.common.tag.SpongeTagTemplateFactory; import org.spongepowered.common.util.SpongeAABB; import org.spongepowered.common.util.SpongeDamageSourceFactory; import org.spongepowered.common.util.SpongeMinecraftDayTime; @@ -168,11 +175,11 @@ import org.spongepowered.common.world.generation.config.flat.SpongeLayerConfigFactory; import org.spongepowered.common.world.generation.config.noise.SpongeSurfaceRulesFactory; import org.spongepowered.common.world.generation.structure.jigsaw.SpongeJigsawFactory; +import org.spongepowered.common.world.generation.structure.jigsaw.SpongeProcessorFactory; import org.spongepowered.common.world.portal.SpongePortalLogicFactory; import org.spongepowered.common.world.schematic.SpongePaletteReferenceFactory; import org.spongepowered.common.world.server.SpongeServerLocation; import org.spongepowered.common.world.server.SpongeServerLocationCreatorFactory; -import org.spongepowered.common.world.server.SpongeWorldTemplate; import org.spongepowered.common.world.volume.archetype.entity.SpongeEntityArchetypeEntryFactory; import org.spongepowered.common.world.volume.block.SpongeBlockVolumeFactory; import org.spongepowered.common.world.weather.SpongeWeather; @@ -247,9 +254,9 @@ public void registerDefaultFactories() { .registerFactory(RegistryKey.Factory.class, new SpongeRegistryKey.FactoryImpl()) .registerFactory(RegistryType.Factory.class, new SpongeRegistryType.FactoryImpl()) .registerFactory(RegistryReference.Factory.class, new SpongeRegistryReference.FactoryImpl()) - .registerFactory(DataPackType.Factory.class, new SpongeDataPackType.FactoryImpl()) .registerFactory(BlockVolumeFactory.class, new SpongeBlockVolumeFactory()) .registerFactory(DamageSource.Factory.class, new SpongeDamageSourceFactory()) + .registerFactory(DamageStepType.Factory.class, SpongeDamageStepType::new) .registerFactory(PaletteReference.Factory.class, new SpongePaletteReferenceFactory()) .registerFactory(EntityArchetypeEntry.Factory.class, new SpongeEntityArchetypeEntryFactory()) .registerFactory(ServerLocationCreator.Factory.class, new SpongeServerLocationCreatorFactory()) @@ -258,7 +265,6 @@ public void registerDefaultFactories() { .registerFactory(BiomeAttributes.Factory.class, new SpongeBiomeAttributesFactory()) .registerFactory(BiomeProvider.Factory.class, new SpongeBiomeProviderFactory()) .registerFactory(WorldTypeEffect.Factory.class, new SpongeWorldTypeEffect.FactoryImpl()) - .registerFactory(WorldTemplate.Factory.class, new SpongeWorldTemplate.FactoryImpl()) .registerFactory(LayerConfig.Factory.class, new SpongeLayerConfigFactory()) .registerFactory(SurfaceRule.Factory.class, new SpongeSurfaceRulesFactory()) .registerFactory(ChunkGenerator.Factory.class, new SpongeChunkGeneratorFactory()) @@ -268,7 +274,6 @@ public void registerDefaultFactories() { .registerFactory(DisplaySlot.Factory.class, new SpongeDisplaySlotFactory()) .registerFactory(Weather.Factory.class, new SpongeWeather.FactoryImpl()) .registerFactory(NodeTree.Factory.class, new SpongeNodeTree.FactoryImpl()) - .registerFactory(TagTemplate.Factory.class, new SpongeTagTemplateFactory()) .registerFactory(Tag.Factory.class, new SpongeTagFactory()) .registerFactory(EventListenerRegistration.Factory.class, new SpongeEventListenerRegistration.FactoryImpl()) .registerFactory(CommandResult.Factory.class, new SpongeCommandResultFactory()) @@ -284,15 +289,21 @@ public void registerDefaultFactories() { .registerFactory(DefaultWorldKeys.Factory.class, new SpongeDefaultWorldKeysFactory()) .registerFactory(PositionSource.Factory.class, new SpongePositionSourceFactory()) .registerFactory(JigsawPoolElement.Factory.class, new SpongeJigsawFactory()) + .registerFactory(Processor.Factory.class, new SpongeProcessorFactory()) .registerFactory(ParticleConfig.Factory.class, new SpongeParticleConfigFactory()) .registerFactory(SoundConfig.Factory.class, new SpongeSoundConfigFactory()) .registerFactory(NaturalSpawnCost.Factory.class, new SpongeNaturalSpawnerCostFactory()) .registerFactory(NaturalSpawner.Factory.class, new SpongeNaturalSpawnerFactory()) .registerFactory(ScoreFormat.Factory.class, new SpongeScoreFormatFactory()) .registerFactory(ToolRule.Factory.class, new SpongeToolRuleFactory()) + .registerFactory(ItemAttribute.Factory.class, new SpongeItemAttributeFactory()) + .registerFactory(ItemAction.Factory.class, new SpongeItemActionFactory()) .registerFactory(PortalLogic.Factory.class, new SpongePortalLogicFactory()) .registerFactory(RecipeInput.Factory.class, new SpongeRecipeInputFactory()) .registerFactory(ArmorTrim.Factory.class, new SpongeArmorTrimFactory()) + .registerFactory(RegistryRegistrationSet.Factory.class, new SpongeRegistryRegistrationSet.FactoryImpl()) + .registerFactory(ShieldDamageReduction.Factory.class, new SpongeShieldDamageReductionFactory()) + .registerFactory(ShieldItemDamageFunction.Factory.class, new SpongeShieldItemDamageFunctionFactory()) ; } } diff --git a/src/main/java/org/spongepowered/common/registry/SpongeRegistries.java b/src/main/java/org/spongepowered/common/registry/SpongeRegistries.java index fcde5c162f6..29ba343d72a 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeRegistries.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeRegistries.java @@ -26,9 +26,9 @@ import net.minecraft.commands.CommandBuildContext; import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.flag.FeatureFlagSet; -import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.advancement.Advancement; +import org.spongepowered.api.item.recipe.Recipe; import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.service.economy.Currency; import org.spongepowered.common.registry.loader.CommandRegistryLoader; @@ -37,6 +37,8 @@ import org.spongepowered.common.registry.loader.SpongeRegistryLoader; import org.spongepowered.common.registry.loader.VanillaRegistryLoader; +import java.util.List; + public final class SpongeRegistries { // During Bootstrap @@ -61,7 +63,6 @@ public static void registerEarlyGlobalRegistries(final SpongeRegistryHolder hold holder.createFrozenRegistry(RegistryTypes.BODY_PART, SpongeRegistryLoader.bodyPart()); holder.createFrozenRegistry(RegistryTypes.CLICK_TYPE, SpongeRegistryLoader.clickType()); holder.createFrozenRegistry(RegistryTypes.CHUNK_REGENERATE_FLAG, SpongeRegistryLoader.chunkRegenerateFlag()); - holder.createFrozenRegistry(RegistryTypes.DAMAGE_MODIFIER_TYPE, SpongeRegistryLoader.damageModifierType()); holder.createFrozenRegistry(RegistryTypes.DISMOUNT_TYPE, SpongeRegistryLoader.dismountType()); holder.createFrozenRegistry(RegistryTypes.GOAL_EXECUTOR_TYPE, SpongeRegistryLoader.goalExecutorType()); holder.createFrozenRegistry(RegistryTypes.GOAL_TYPE, SpongeRegistryLoader.goalType()); @@ -83,6 +84,7 @@ public static void registerEarlyGlobalRegistries(final SpongeRegistryHolder hold holder.createFrozenRegistry(RegistryTypes.MAP_DECORATION_ORIENTATION, SpongeRegistryLoader.mapDecorationOrientation()); holder.createFrozenRegistry(RegistryTypes.MAP_SHADE, SpongeRegistryLoader.mapShade()); holder.createFrozenRegistry(RegistryTypes.NOISE_CONFIG, SpongeRegistryLoader.noiseConfig()); + holder.createFrozenRegistry(RegistryTypes.REGISTRY_KEYED_VALUE_PARAMETER, CommandRegistryLoader.valueParameter()); SpongeRegistries.registerEarlyDynamicRegistries(holder); } @@ -92,22 +94,22 @@ private static void registerEarlyDynamicRegistries(final SpongeRegistryHolder ho holder.createRegistry(RegistryTypes.COMMAND_REGISTRAR_TYPE, CommandRegistryLoader.commandRegistrarType(), true); holder.createRegistry(RegistryTypes.PLACEHOLDER_PARSER, DynamicSpongeRegistryLoader.placeholderParser(), true); holder.createRegistry(RegistryTypes.TELEPORT_HELPER_FILTER, DynamicSpongeRegistryLoader.teleportHelperFilter(), true); + holder.createRegistry(RegistryTypes.DAMAGE_STEP_TYPE, SpongeRegistryLoader.damageStepType(), true); } + @SuppressWarnings({"unchecked", "rawtypes"}) + public static void registerServerRegistries(final SpongeRegistryHolder holder, final FeatureFlagSet featureFlags) { + holder.createFrozenRegistry(RegistryTypes.COMMAND_TREE_NODE_TYPE, (h) -> + CommandRegistryLoader.clientCompletionKey(CommandBuildContext.simple( + new RegistryAccess.ImmutableRegistryAccess((List) h.streamRegistries().toList()), featureFlags)), RegistryTypes.WORLD_ARCHETYPE_TYPE); - public static void registerGlobalRegistriesDimensionLayer(final SpongeRegistryHolder holder, final RegistryAccess.Frozen registryAccess, final FeatureFlagSet featureFlags) { - if (holder.findRegistry(RegistryTypes.COMMAND_TREE_NODE_TYPE).isPresent()) { - return; // Already done - } - final RegistryAccess.ImmutableRegistryAccess builtInRegistryAccess = new RegistryAccess.ImmutableRegistryAccess(BuiltInRegistries.REGISTRY.stream().toList()); - final CommandBuildContext cbCtx = CommandBuildContext.simple(builtInRegistryAccess, featureFlags); - holder.createOrReplaceFrozenRegistry(RegistryTypes.COMMAND_TREE_NODE_TYPE, CommandRegistryLoader.clientCompletionKey(cbCtx)); - holder.createOrReplaceFrozenRegistry(RegistryTypes.REGISTRY_KEYED_VALUE_PARAMETER, CommandRegistryLoader.valueParameter(cbCtx)); + holder.createFrozenRegistry(RegistryTypes.FLAT_GENERATOR_CONFIG, SpongeRegistryLoader::flatGeneratorConfig, RegistryTypes.WORLD_ARCHETYPE_TYPE); - holder.createOrReplaceFrozenRegistry(RegistryTypes.FLAT_GENERATOR_CONFIG, SpongeRegistryLoader.flatGeneratorConfig(registryAccess)); + SpongeRegistries.registerDynamicServerRegistries(holder); } - public static void registerServerRegistries(final RegistryHolder holder) { + private static void registerDynamicServerRegistries(final SpongeRegistryHolder holder) { + holder.createRegistry(RegistryTypes.ADVANCEMENT, (RegistryLoader) null, true); + holder.createRegistry(RegistryTypes.RECIPE, (RegistryLoader>) null, true); } - } diff --git a/src/main/java/org/spongepowered/common/registry/SpongeRegistryDependencyEntry.java b/src/main/java/org/spongepowered/common/registry/SpongeRegistryDependencyEntry.java new file mode 100644 index 00000000000..fb5b1bc3692 --- /dev/null +++ b/src/main/java/org/spongepowered/common/registry/SpongeRegistryDependencyEntry.java @@ -0,0 +1,43 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.registry; + +import net.minecraft.util.DependencySorter; +import org.spongepowered.api.registry.RegistryType; + +import java.util.Collection; +import java.util.function.Consumer; + +public record SpongeRegistryDependencyEntry(T cookie, Collection> dependencies) implements DependencySorter.Entry> { + + @Override + public void visitRequiredDependencies(final Consumer> consumer) { + this.dependencies.forEach(consumer); + } + + @Override + public void visitOptionalDependencies(final Consumer> consumer) { + } +} diff --git a/src/main/java/org/spongepowered/common/registry/SpongeRegistryHolder.java b/src/main/java/org/spongepowered/common/registry/SpongeRegistryHolder.java index 903455675ac..3aa0ddf4dbb 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeRegistryHolder.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeRegistryHolder.java @@ -24,21 +24,26 @@ */ package org.spongepowered.common.registry; +import net.minecraft.core.HolderLookup; import net.minecraft.core.MappedRegistry; import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.world.flag.FeatureFlagSet; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.common.bridge.core.WritableRegistryBridge; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.BiConsumer; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Stream; -public interface SpongeRegistryHolder extends RegistryHolder { +public interface SpongeRegistryHolder extends RegistryHolder, HolderLookup.Provider { RegistryHolderLogic registryHolder(); @@ -46,14 +51,25 @@ default void setRootMinecraftRegistry(Registry> registry) { this.registryHolder().setRootMinecraftRegistry(registry); } + default void setRootMinecraftRegistry(RegistryAccess registryAccess) { + this.registryHolder().setRootMinecraftRegistry(registryAccess); + } + default org.spongepowered.api.registry.Registry createRegistry(final RegistryType type, final @Nullable InitialRegistryData defaultValues, - final boolean isDynamic, final @Nullable BiConsumer, T> callback, final boolean replace) { - return this.registryHolder().createRegistry(type, defaultValues, this.registryHolder().registrySupplier(isDynamic, callback), replace); + final boolean isDynamic, final @Nullable BiConsumer, T> callback) { + return this.registryHolder().createRegistry(type, defaultValues, this.registryHolder().registrySupplier(isDynamic, callback)); } default org.spongepowered.api.registry.Registry createRegistry(final RegistryType type, final @Nullable Supplier> defaultValues, final boolean isDynamic) { - return this.createRegistry(type, InitialRegistryData.noIds(defaultValues), isDynamic, null, false); + return this.createRegistry(type, InitialRegistryData.noIds(defaultValues), isDynamic, null); + } + + default org.spongepowered.api.registry.Registry createRegistry(final RegistryType type, final @Nullable Function> defaultValues, + final boolean isDynamic, final RegistryType... dependencies) { + final org.spongepowered.api.registry.Registry registry = this.createRegistry(type, (RegistryLoader) null, isDynamic, null); + ((WritableRegistryBridge) registry).bridge$addDependencies(() -> InitialRegistryData.noIds(() -> defaultValues.apply(this.registryHolder())), dependencies); + return registry; } default org.spongepowered.api.registry.Registry createRegistry(final RegistryType type, final RegistryLoader loader) { @@ -68,17 +84,21 @@ default org.spongepowered.api.registry.Registry createFrozenRegistry(fina return registry; } - default org.spongepowered.api.registry.Registry createOrReplaceFrozenRegistry(final RegistryType type, final RegistryLoader loader) { - final org.spongepowered.api.registry.Registry registry = this.createRegistry(type, loader, false, null, true); - if (registry instanceof MappedRegistry toFreeze) { - toFreeze.freeze(); - } + default org.spongepowered.api.registry.Registry createFrozenRegistry(final RegistryType type, + final Function> loader, final RegistryType... dependencies) { + final org.spongepowered.api.registry.Registry registry = this.createRegistry(type, (RegistryLoader) null, false); + ((WritableRegistryBridge) registry).bridge$addDependencies(() -> loader.apply(this.registryHolder()), dependencies); return registry; } default org.spongepowered.api.registry.Registry createRegistry(final RegistryType type, final RegistryLoader loader, final boolean isDynamic) { - return this.createRegistry(type, loader, isDynamic, null, false); + return this.createRegistry(type, loader, isDynamic, null); + } + + @Override + default Stream> streamRegistries() { + return this.registryHolder().streamRegistries(); } @Override @@ -95,4 +115,19 @@ default Optional> findRegistry(fi default org.spongepowered.api.registry.Registry registry(final RegistryType type) { return this.registryHolder().registry(Objects.requireNonNull(type, "type")); } + + default FeatureFlagSet featureFlagSet() { + return this.registryHolder().featureFlagSet(); + } + + @Override + default Stream>> listRegistryKeys() { + return this.registryHolder().listRegistryKeys(); + } + + @Override + default Optional> lookup( + final net.minecraft.resources.ResourceKey> resourceKey) { + return this.registryHolder().lookup(resourceKey); + } } diff --git a/src/main/java/org/spongepowered/common/registry/SpongeRegistryKey.java b/src/main/java/org/spongepowered/common/registry/SpongeRegistryKey.java index 66e72475fd7..de085046aec 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeRegistryKey.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeRegistryKey.java @@ -30,6 +30,7 @@ import org.spongepowered.api.registry.RegistryKey; import org.spongepowered.api.registry.RegistryReference; import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.common.SpongeCommon; import java.util.Objects; import java.util.StringJoiner; @@ -65,6 +66,11 @@ public final DefaultedRegistryReference asDefaultedReference(fi return new SpongeDefaultedRegistryReference<>((RegistryKey) this, defaultHolder); } + @Override + public final DefaultedRegistryReference asScopedReference() { + return new SpongeDefaultedRegistryReference<>((RegistryKey) this, SpongeCommon::scopedHolder); + } + @Override public int hashCode() { return Objects.hash(this.registry, this.location); diff --git a/src/main/java/org/spongepowered/common/registry/SpongeRegistryRegistrationSet.java b/src/main/java/org/spongepowered/common/registry/SpongeRegistryRegistrationSet.java new file mode 100644 index 00000000000..03aa2ca963c --- /dev/null +++ b/src/main/java/org/spongepowered/common/registry/SpongeRegistryRegistrationSet.java @@ -0,0 +1,80 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.registry; + +import com.google.common.collect.ImmutableMap; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.registry.DefaultedRegistryReference; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryRegistrationSet; +import org.spongepowered.api.registry.RegistryType; + +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +public record SpongeRegistryRegistrationSet(RegistryType registryType, Map> values) implements RegistryRegistrationSet { + + public static final class BuilderImpl implements RegistryRegistrationSet.Builder { + + private final RegistryType registryType; + private final Supplier defaultHolder; + + private ImmutableMap.Builder> builder = ImmutableMap.builder(); + + BuilderImpl(final RegistryType registryType, final Supplier defaultHolder) { + this.registryType = registryType; + this.defaultHolder = defaultHolder; + } + + @SuppressWarnings("unchecked") + @Override + public DefaultedRegistryReference register(final ResourceKey key, final Function value) { + this.builder.put(key, (Function) value); + return new SpongeDefaultedRegistryReference<>(new SpongeRegistryKey<>((RegistryType) this.registryType, key), this.defaultHolder); + } + + @Override + public Builder reset() { + this.builder = ImmutableMap.builder(); + return this; + } + + @NotNull + @Override + public RegistryRegistrationSet build() { + return new SpongeRegistryRegistrationSet<>(this.registryType, this.builder.build()); + } + } + + public static final class FactoryImpl implements RegistryRegistrationSet.Factory { + + @Override + public Builder builder(final RegistryType registryType, final Supplier defaultHolder) { + return new BuilderImpl<>(registryType, defaultHolder); + } + } +} diff --git a/src/main/java/org/spongepowered/common/registry/SpongeRegistryType.java b/src/main/java/org/spongepowered/common/registry/SpongeRegistryType.java index be1c0fdaa89..d7327b0942d 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeRegistryType.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeRegistryType.java @@ -28,6 +28,7 @@ import org.spongepowered.api.registry.DefaultedRegistryType; import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.common.SpongeCommon; import java.util.Objects; import java.util.StringJoiner; @@ -58,6 +59,11 @@ public final DefaultedRegistryType asDefaultedType(final Suppli return new SpongeDefaultedRegistryType<>(this.root, this.location, Objects.requireNonNull(defaultHolder, "defaultHolder")); } + @Override + public DefaultedRegistryType asScopedType() { + return new SpongeDefaultedRegistryType<>(this.root, this.location, SpongeCommon::scopedHolder); + } + @Override public boolean equals(final Object o) { if (this == o) { diff --git a/src/main/java/org/spongepowered/common/registry/SpongeResourceKeyBuilder.java b/src/main/java/org/spongepowered/common/registry/SpongeResourceKeyBuilder.java index 87c23eab65e..09849a23159 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeResourceKeyBuilder.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeResourceKeyBuilder.java @@ -25,8 +25,8 @@ package org.spongepowered.common.registry; -import net.minecraft.ResourceLocationException; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.IdentifierException; +import net.minecraft.resources.Identifier; import org.spongepowered.api.ResourceKey; import org.spongepowered.common.util.Preconditions; @@ -57,9 +57,9 @@ public ResourceKey build() throws IllegalStateException { Preconditions.checkState(this.value != null, "Value cannot be empty"); try { - final ResourceLocation resourceLocation = ResourceLocation.fromNamespaceAndPath(this.namespace, this.value); - return (ResourceKey) (Object) resourceLocation; - } catch (ResourceLocationException e) { + final Identifier id = Identifier.fromNamespaceAndPath(this.namespace, this.value); + return (ResourceKey) (Object) id; + } catch (IdentifierException e) { throw new IllegalStateException(e); } } diff --git a/src/main/java/org/spongepowered/common/registry/SpongeResourceKeyFactory.java b/src/main/java/org/spongepowered/common/registry/SpongeResourceKeyFactory.java index 325fd990a89..a57d6da7ca2 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeResourceKeyFactory.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeResourceKeyFactory.java @@ -24,8 +24,8 @@ */ package org.spongepowered.common.registry; -import net.minecraft.ResourceLocationException; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.IdentifierException; +import net.minecraft.resources.Identifier; import org.spongepowered.api.ResourceKey; import org.spongepowered.plugin.PluginContainer; @@ -34,9 +34,9 @@ public final class SpongeResourceKeyFactory implements ResourceKey.Factory { @Override public ResourceKey of(final String namespace, final String value) { try { - final ResourceLocation resourceLocation = ResourceLocation.fromNamespaceAndPath(namespace, value); - return (ResourceKey) (Object) resourceLocation; - } catch (ResourceLocationException e) { + final Identifier id = Identifier.fromNamespaceAndPath(namespace, value); + return (ResourceKey) (Object) id; + } catch (IdentifierException e) { throw new IllegalStateException(e); } } @@ -44,9 +44,9 @@ public ResourceKey of(final String namespace, final String value) { @Override public ResourceKey of(final PluginContainer plugin, final String value) { try { - final ResourceLocation resourceLocation = ResourceLocation.fromNamespaceAndPath(plugin.metadata().id(), value); - return (ResourceKey) (Object) resourceLocation; - } catch (ResourceLocationException e) { + final Identifier id = Identifier.fromNamespaceAndPath(plugin.metadata().id(), value); + return (ResourceKey) (Object) id; + } catch (IdentifierException e) { throw new IllegalStateException(e); } } @@ -54,9 +54,9 @@ public ResourceKey of(final PluginContainer plugin, final String value) { @Override public ResourceKey resolve(final String formatted) { try { - final ResourceLocation resourceLocation = ResourceLocation.parse(formatted); - return (ResourceKey) (Object) resourceLocation; - } catch (ResourceLocationException e) { + final Identifier id = Identifier.parse(formatted); + return (ResourceKey) (Object) id; + } catch (IdentifierException e) { throw new IllegalStateException(e); } } diff --git a/src/main/java/org/spongepowered/common/registry/loader/CommandRegistryLoader.java b/src/main/java/org/spongepowered/common/registry/loader/CommandRegistryLoader.java index 3b443fb70a2..176c76c9097 100644 --- a/src/main/java/org/spongepowered/common/registry/loader/CommandRegistryLoader.java +++ b/src/main/java/org/spongepowered/common/registry/loader/CommandRegistryLoader.java @@ -40,7 +40,7 @@ import net.minecraft.commands.arguments.DimensionArgument; import net.minecraft.commands.arguments.EntityArgument; import net.minecraft.commands.arguments.GameProfileArgument; -import net.minecraft.commands.arguments.ResourceLocationArgument; +import net.minecraft.commands.arguments.IdentifierArgument; import net.minecraft.commands.arguments.ScoreHolderArgument; import net.minecraft.commands.arguments.TimeArgument; import net.minecraft.commands.arguments.UuidArgument; @@ -58,8 +58,9 @@ import net.minecraft.commands.synchronization.brigadier.FloatArgumentInfo; import net.minecraft.commands.synchronization.brigadier.IntegerArgumentInfo; import net.minecraft.commands.synchronization.brigadier.LongArgumentInfo; +import net.minecraft.core.HolderLookup; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.phys.Vec2; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; @@ -84,6 +85,7 @@ import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.common.accessor.commands.arguments.DimensionArgumentAccessor; import org.spongepowered.common.adventure.SpongeAdventure; +import org.spongepowered.common.command.brigadier.argument.BindableValueParameter; import org.spongepowered.common.command.brigadier.argument.ClientNativeArgumentParser; import org.spongepowered.common.command.parameter.managed.clientcompletion.SpongeClientCompletionType; import org.spongepowered.common.command.parameter.managed.operator.SpongeAdditionOperator; @@ -127,18 +129,19 @@ import org.spongepowered.math.vector.Vector3d; import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.util.function.Function; import java.util.stream.Collectors; public final class CommandRegistryLoader { @SuppressWarnings("ConstantConditions") - public static RegistryLoader> valueParameter(final CommandBuildContext cbCtx) { + public static RegistryLoader> valueParameter() { return RegistryLoader.of(l -> { l.add(ResourceKeyedValueParameters.BIG_DECIMAL, SpongeBigDecimalValueParameter::new); l.add(ResourceKeyedValueParameters.BIG_INTEGER, SpongeBigIntegerValueParameter::new); - l.add(ResourceKeyedValueParameters.BLOCK_STATE, k -> ClientNativeArgumentParser.createConverter(k, BlockStateArgument.block(cbCtx), (reader, cause, state) -> (BlockState) state.getState())); + l.add(ResourceKeyedValueParameters.BLOCK_STATE, k -> new BindableValueParameter<>(k, h -> ClientNativeArgumentParser.createConverter(k, BlockStateArgument.block(CommandBuildContext.simple((HolderLookup.Provider) h, ((CommandBuildContext) h).enabledFeatures())), (reader, cause, state) -> (BlockState) state.getState()))); l.add(ResourceKeyedValueParameters.BOOLEAN, k -> ClientNativeArgumentParser.createIdentity(k, BoolArgumentType.bool())); l.add(ResourceKeyedValueParameters.COLOR, SpongeColorValueParameter::new); l.add(ResourceKeyedValueParameters.DATA_CONTAINER, SpongeDataContainerValueParameter::new); @@ -149,7 +152,7 @@ public static RegistryLoader> valueParameter(final CommandBuil l.add(ResourceKeyedValueParameters.GAME_PROFILE, SpongeGameProfileValueParameter::new); l.add(ResourceKeyedValueParameters.INTEGER, k -> ClientNativeArgumentParser.createIdentity(k, IntegerArgumentType.integer())); l.add(ResourceKeyedValueParameters.IP, SpongeIPAddressValueParameter::new); - l.add(ResourceKeyedValueParameters.ITEM_STACK_SNAPSHOT, k -> ClientNativeArgumentParser.createConverter(k, ItemArgument.item(cbCtx), (reader, cause, converter) -> new SpongeItemStackSnapshot((ItemStack) (Object) converter.createItemStack(1, true)))); + l.add(ResourceKeyedValueParameters.ITEM_STACK_SNAPSHOT, k -> new BindableValueParameter<>(k, h -> ClientNativeArgumentParser.createConverter(k, ItemArgument.item(CommandBuildContext.simple((HolderLookup.Provider) h, ((CommandBuildContext) h).enabledFeatures())), (reader, cause, converter) -> new SpongeItemStackSnapshot((ItemStack) (Object) converter.createItemStack(1))))); l.add(ResourceKeyedValueParameters.LOCATION, SpongeServerLocationValueParameter::new); l.add(ResourceKeyedValueParameters.LONG, k -> ClientNativeArgumentParser.createIdentity(k, LongArgumentType.longArg())); l.add(ResourceKeyedValueParameters.MANY_ENTITIES, k -> ClientNativeArgumentParser.createConverter(k, EntityArgument.entities(), (reader, cause, selector) -> selector.findEntities((CommandSourceStack) cause).stream().map(x -> (Entity) x).collect( @@ -161,7 +164,7 @@ public static RegistryLoader> valueParameter(final CommandBuil l.add(ResourceKeyedValueParameters.PLAYER, k -> ClientNativeArgumentParser.createConverter(k, EntityArgument.player(), (reader, cause, selector) -> (Player) selector.findSinglePlayer((CommandSourceStack) cause))); l.add(ResourceKeyedValueParameters.PLUGIN, SpongePluginContainerValueParameter::new); l.add(ResourceKeyedValueParameters.REMAINING_JOINED_STRINGS, k -> ClientNativeArgumentParser.createIdentity(k, StringArgumentType.greedyString())); - l.add(ResourceKeyedValueParameters.RESOURCE_KEY, k -> ClientNativeArgumentParser.createConverter(k, ResourceLocationArgument.id(), (reader, cause, resourceLocation) -> (ResourceKey) (Object) resourceLocation)); + l.add(ResourceKeyedValueParameters.RESOURCE_KEY, k -> ClientNativeArgumentParser.createConverter(k, IdentifierArgument.id(), (reader, cause, Identifier) -> (ResourceKey) (Object) Identifier)); l.add(ResourceKeyedValueParameters.ROTATION, k -> ClientNativeArgumentParser.createConverter(k, RotationArgument.rotation(), (reader, cause, coords) -> { final Vec2 rotation = coords.getRotation((CommandSourceStack) cause); return new Vector3d(rotation.x, rotation.y, 0); @@ -172,13 +175,13 @@ public static RegistryLoader> valueParameter(final CommandBuil l.add(ResourceKeyedValueParameters.TARGET_PLAYER, k -> new SpongeTargetEntityValueParameter(k, true)); l.add(ResourceKeyedValueParameters.TEXT_FORMATTING_CODE, k -> ClientNativeArgumentParser.createConverter(k, StringArgumentType.string(), (reader, cause, result) -> LegacyComponentSerializer.legacyAmpersand().deserialize(result))); l.add(ResourceKeyedValueParameters.TEXT_FORMATTING_CODE_ALL, k -> ClientNativeArgumentParser.createConverter(k, StringArgumentType.greedyString(), (reader, cause, result) -> LegacyComponentSerializer.legacyAmpersand().deserialize(result))); - l.add(ResourceKeyedValueParameters.TEXT_JSON, k -> ClientNativeArgumentParser.createConverter(k, ComponentArgument.textComponent(cbCtx), (reader, cause, result) -> SpongeAdventure.asAdventure(result))); + l.add(ResourceKeyedValueParameters.TEXT_JSON, k -> new BindableValueParameter<>(k, h -> ClientNativeArgumentParser.createConverter(k, ComponentArgument.textComponent(CommandBuildContext.simple((HolderLookup.Provider) h, ((CommandBuildContext) h).enabledFeatures())), (reader, cause, result) -> SpongeAdventure.asAdventure(result)))); l.add(ResourceKeyedValueParameters.TEXT_JSON_ALL, k -> ClientNativeArgumentParser.createConverter(k, StringArgumentType.greedyString(), (reader, cause, result) -> GsonComponentSerializer.gson().deserialize(result))); l.add(ResourceKeyedValueParameters.URL, k -> ClientNativeArgumentParser.createConverter(k, StringArgumentType.string(), (reader, cause, input) -> { try { - return new URL(input); - } catch (final MalformedURLException ex) { + return new URI(input).toURL(); + } catch (final MalformedURLException | URISyntaxException ex) { throw new SimpleCommandExceptionType(net.minecraft.network.chat.Component.literal("Could not parse " + input + " as a URL")) .createWithContext(reader); } @@ -209,7 +212,7 @@ public static RegistryLoader clientCompletionType() { l.add(ClientCompletionTypes.SNBT, k -> new SpongeClientCompletionType(CompoundTagArgument.compoundTag())); l.add(ClientCompletionTypes.NONE, k -> SpongeClientCompletionType.NONE); l.add(ClientCompletionTypes.REMAINING_JOINED_STRINGS, k -> new SpongeClientCompletionType(StringArgumentType.greedyString())); - l.add(ClientCompletionTypes.RESOURCE_KEY, k -> new SpongeClientCompletionType(ResourceLocationArgument.id())); + l.add(ClientCompletionTypes.RESOURCE_KEY, k -> new SpongeClientCompletionType(IdentifierArgument.id())); l.add(ClientCompletionTypes.STRING, k -> new SpongeClientCompletionType(StringArgumentType.string())); l.add(ClientCompletionTypes.WHOLE_NUMBER, k -> new SpongeClientCompletionType(LongArgumentType.longArg())); }); @@ -218,7 +221,6 @@ public static RegistryLoader clientCompletionType() { public static RegistryLoader clientSuggestionProvider() { return RegistryLoader.of(l -> { // TODO based on SuggestionProviders.PROVIDERS_BY_NAME instead - l.add(CommandCompletionProviders.ALL_RECIPES, k -> (CommandCompletionProvider) SuggestionProviders.ALL_RECIPES); l.add(CommandCompletionProviders.AVAILABLE_SOUNDS, k -> (CommandCompletionProvider) SuggestionProviders.AVAILABLE_SOUNDS); l.add(CommandCompletionProviders.SUMMONABLE_ENTITIES, k -> (CommandCompletionProvider) SuggestionProviders.SUMMONABLE_ENTITIES); }); @@ -245,7 +247,8 @@ public static RegistryLoader> clientCompletionKey(final C l.add(CommandTreeNodeTypes.BLOCK_PREDICATE, k -> new SpongeBasicCommandTreeNodeType(k, fn.apply(k))); l.add(CommandTreeNodeTypes.ITEM_STACK, k -> new SpongeBasicCommandTreeNodeType(k, fn.apply(k))); l.add(CommandTreeNodeTypes.ITEM_PREDICATE, k -> new SpongeBasicCommandTreeNodeType(k, fn.apply(k))); - l.add(CommandTreeNodeTypes.COLOR, k -> new SpongeBasicCommandTreeNodeType(k, fn.apply(k))); + l.add(CommandTreeNodeTypes.TEAM_COLOR, k -> new SpongeBasicCommandTreeNodeType(k, fn.apply(k))); + l.add(CommandTreeNodeTypes.HEX_COLOR, k -> new SpongeBasicCommandTreeNodeType(k, fn.apply(k))); l.add(CommandTreeNodeTypes.COMPONENT, k -> new SpongeBasicCommandTreeNodeType(k, fn.apply(k))); l.add(CommandTreeNodeTypes.MESSAGE, k -> new SpongeBasicCommandTreeNodeType(k, fn.apply(k))); l.add(CommandTreeNodeTypes.NBT_COMPOUND_TAG, k -> new SpongeBasicCommandTreeNodeType(k, fn.apply(k))); @@ -294,7 +297,7 @@ public static RegistryLoader> commandRegistrarType() { // Helper static ArgumentType argumentTypeFromKey(ResourceKey key, CommandBuildContext ctx) { - final ArgumentTypeInfo argumentTypeInfo = BuiltInRegistries.COMMAND_ARGUMENT_TYPE.get((ResourceLocation) (Object) key); + final ArgumentTypeInfo argumentTypeInfo = BuiltInRegistries.COMMAND_ARGUMENT_TYPE.getValue((Identifier) (Object) key); if (argumentTypeInfo instanceof SingletonArgumentInfo s) { return s.unpack(null).instantiate(ctx); } diff --git a/src/main/java/org/spongepowered/common/registry/loader/SpongeCommonRegistryLoader.java b/src/main/java/org/spongepowered/common/registry/loader/SpongeCommonRegistryLoader.java index eb6b5a2ff83..475053f0f5a 100644 --- a/src/main/java/org/spongepowered/common/registry/loader/SpongeCommonRegistryLoader.java +++ b/src/main/java/org/spongepowered/common/registry/loader/SpongeCommonRegistryLoader.java @@ -48,6 +48,7 @@ public class SpongeCommonRegistryLoader { l.add(TransactionTypes.NEIGHBOR_NOTIFICATION, k -> new NoOpTransactionType<>(false, k.value().toUpperCase(Locale.ROOT))); l.add(TransactionTypes.SCHEDULE_BLOCK_UPDATE, k -> new NoOpTransactionType<>(false, k.value().toUpperCase(Locale.ROOT))); l.add(TransactionTypes.SLOT_CHANGE, k -> new NoOpTransactionType<>(false, k.value().toUpperCase(Locale.ROOT))); + l.add(TransactionTypes.INTERACT_BLOCK_SECONDARY, k -> new NoOpTransactionType<>(false, k.value().toUpperCase(Locale.ROOT))); l.add(TransactionTypes.SPAWN_ENTITY, k -> new NoOpTransactionType<>(false, k.value().toUpperCase(Locale.ROOT))); }); } diff --git a/src/main/java/org/spongepowered/common/registry/loader/SpongeRegistryLoader.java b/src/main/java/org/spongepowered/common/registry/loader/SpongeRegistryLoader.java index de3bfb4931a..619bf64f024 100644 --- a/src/main/java/org/spongepowered/common/registry/loader/SpongeRegistryLoader.java +++ b/src/main/java/org/spongepowered/common/registry/loader/SpongeRegistryLoader.java @@ -24,8 +24,6 @@ */ package org.spongepowered.common.registry.loader; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.Registries; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorPreset; import net.minecraft.world.level.material.MapColor; @@ -67,8 +65,8 @@ import org.spongepowered.api.event.cause.entity.MovementTypes; import org.spongepowered.api.event.cause.entity.SpawnType; import org.spongepowered.api.event.cause.entity.SpawnTypes; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierType; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierTypes; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; import org.spongepowered.api.item.inventory.ItemStackSnapshot; import org.spongepowered.api.item.inventory.menu.ClickType; import org.spongepowered.api.item.inventory.menu.ClickTypes; @@ -80,7 +78,10 @@ import org.spongepowered.api.map.color.MapShades; import org.spongepowered.api.map.decoration.orientation.MapDecorationOrientation; import org.spongepowered.api.map.decoration.orientation.MapDecorationOrientations; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.registry.RegistryKey; +import org.spongepowered.api.registry.RegistryType; import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.service.ban.Ban; import org.spongepowered.api.service.ban.BanType; @@ -125,7 +126,7 @@ import org.spongepowered.common.event.cause.entity.SpongeMovementType; import org.spongepowered.common.event.cause.entity.SpongeSpawnType; import org.spongepowered.common.event.cause.entity.SpongeSpawnTypes; -import org.spongepowered.common.event.cause.entity.damage.SpongeDamageModifierType; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageStepType; import org.spongepowered.common.inventory.menu.handler.SpongeClickType; import org.spongepowered.common.inventory.query.SpongeOneParamQueryType; import org.spongepowered.common.inventory.query.SpongeQueryTypes; @@ -218,25 +219,26 @@ public static RegistryLoader> clickType() { ))); } - public static RegistryLoader damageModifierType() { - return RegistryLoader.of(l -> l.mapping(SpongeDamageModifierType::new, m -> m.add( - DamageModifierTypes.ABSORPTION, - DamageModifierTypes.ARMOR, - DamageModifierTypes.ARMOR_ENCHANTMENT, - DamageModifierTypes.ATTACK_COOLDOWN, - DamageModifierTypes.CRITICAL_HIT, - DamageModifierTypes.DEFENSIVE_POTION_EFFECT, - DamageModifierTypes.DIFFICULTY, - DamageModifierTypes.HARD_HAT, - DamageModifierTypes.MAGIC, - DamageModifierTypes.NEGATIVE_POTION_EFFECT, - DamageModifierTypes.OFFENSIVE_POTION_EFFECT, - DamageModifierTypes.SHIELD, - DamageModifierTypes.SWEEPING, - DamageModifierTypes.WEAPON_ENCHANTMENT, - DamageModifierTypes.WEAPON_BONUS, - DamageModifierTypes.ATTACK_STRENGTH, - DamageModifierTypes.FREEZING_BONUS + public static RegistryLoader damageStepType() { + return RegistryLoader.of(l -> l.mapping(SpongeDamageStepType::new, m -> m.add( + DamageStepTypes.ABSORPTION, + DamageStepTypes.ARMOR, + DamageStepTypes.ARMOR_ENCHANTMENT, + DamageStepTypes.BASE_COOLDOWN, + DamageStepTypes.CRITICAL_HIT, + DamageStepTypes.DEFENSIVE_POTION_EFFECT, + DamageStepTypes.ENCHANTMENT_COOLDOWN, + DamageStepTypes.END, + DamageStepTypes.FREEZING_BONUS, + DamageStepTypes.HARD_HAT, + DamageStepTypes.MAGIC, + DamageStepTypes.NEGATIVE_POTION_EFFECT, + DamageStepTypes.OFFENSIVE_POTION_EFFECT, + DamageStepTypes.SHIELD, + DamageStepTypes.START, + DamageStepTypes.SWEEPING, + DamageStepTypes.WEAPON_BONUS, + DamageStepTypes.WEAPON_ENCHANTMENT ))); } @@ -288,7 +290,8 @@ public static RegistryLoader movementType() { MovementTypes.ENTITY_TELEPORT, MovementTypes.NATURAL, MovementTypes.PLUGIN, - MovementTypes.PORTAL + MovementTypes.PORTAL, + MovementTypes.SPECTATOR ))); } @@ -383,6 +386,7 @@ public static RegistryLoader> particleOption() { l.add(ParticleOptions.TO_COLOR, k -> new SpongeParticleOption<>(Color.class)); l.add(ParticleOptions.TRAVEL_TIME, k -> new SpongeParticleOption<>(Ticks.class)); l.add(ParticleOptions.VELOCITY, k -> new SpongeParticleOption<>(Vector3d.class)); + l.add(ParticleOptions.TARGET, k -> new SpongeParticleOption<>(Vector3d.class)); }); } @@ -408,6 +412,7 @@ public static RegistryLoader resolveOperation() { return RegistryLoader.of(l -> { l.add(ResolveOperations.CONTEXTUAL_COMPONENTS, SpongeResolveOperation::newContextualComponents); l.add(ResolveOperations.CUSTOM_TRANSLATIONS, SpongeResolveOperation::newCustomTranslations); + l.add(ResolveOperations.VIRTUAL_COMPONENTS, SpongeResolveOperation::newVirtualComponents); }); } @@ -580,13 +585,13 @@ public static RegistryLoader noiseConfig() { }); } - public static RegistryLoader flatGeneratorConfig(RegistryAccess registryAccess) { - final Registry registry = registryAccess.registryOrThrow(Registries.FLAT_LEVEL_GENERATOR_PRESET); - return RegistryLoader.of(l -> { - for (final var entry : registry.entrySet()) { - l.add(RegistryKey.of(RegistryTypes.FLAT_GENERATOR_CONFIG, (ResourceKey) (Object) entry.getKey().location()), () -> (FlatGeneratorConfig) entry.getValue().settings()); - } - }); + public static RegistryLoader flatGeneratorConfig(RegistryHolder registryAccess) { + final RegistryType registryType = RegistryType.of( + (ResourceKey) (Object) Registries.FLAT_LEVEL_GENERATOR_PRESET.registry(), (ResourceKey) (Object) Registries.FLAT_LEVEL_GENERATOR_PRESET.identifier()); + final Registry registry = registryAccess.registry(registryType); + return RegistryLoader.of(l -> + registry.streamEntries().forEach(e -> + l.add(RegistryKey.of(RegistryTypes.FLAT_GENERATOR_CONFIG, e.key()), () -> (FlatGeneratorConfig) e.value().settings()))); } // @formatter:on diff --git a/src/main/java/org/spongepowered/common/registry/loader/VanillaRegistryLoader.java b/src/main/java/org/spongepowered/common/registry/loader/VanillaRegistryLoader.java index f164896ebab..5c2c77c0dc1 100644 --- a/src/main/java/org/spongepowered/common/registry/loader/VanillaRegistryLoader.java +++ b/src/main/java/org/spongepowered/common/registry/loader/VanillaRegistryLoader.java @@ -28,7 +28,7 @@ import net.minecraft.advancements.AdvancementType; import net.minecraft.core.FrontAndTop; import net.minecraft.core.MappedRegistry; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.util.StringRepresentable; import net.minecraft.world.Difficulty; import net.minecraft.world.InteractionHand; @@ -36,30 +36,35 @@ import net.minecraft.world.damagesource.DamageScaling; import net.minecraft.world.entity.Display; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.EquipmentSlotGroup; import net.minecraft.world.entity.HumanoidArm; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.entity.ai.attributes.AttributeModifier; -import net.minecraft.world.entity.animal.Fox; -import net.minecraft.world.entity.animal.MushroomCow; -import net.minecraft.world.entity.animal.Panda; -import net.minecraft.world.entity.animal.Parrot; -import net.minecraft.world.entity.animal.Rabbit; -import net.minecraft.world.entity.animal.TropicalFish; -import net.minecraft.world.entity.animal.horse.Llama; -import net.minecraft.world.entity.animal.horse.Markings; -import net.minecraft.world.entity.animal.horse.Variant; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import net.minecraft.world.entity.animal.cow.MushroomCow; +import net.minecraft.world.entity.animal.equine.Llama; +import net.minecraft.world.entity.animal.equine.Markings; +import net.minecraft.world.entity.animal.equine.Variant; +import net.minecraft.world.entity.animal.fish.Salmon; +import net.minecraft.world.entity.animal.fish.TropicalFish; +import net.minecraft.world.entity.animal.fox.Fox; +import net.minecraft.world.entity.animal.panda.Panda; +import net.minecraft.world.entity.animal.parrot.Parrot; +import net.minecraft.world.entity.animal.rabbit.Rabbit; import net.minecraft.world.entity.boss.enderdragon.phases.EnderDragonPhase; import net.minecraft.world.entity.monster.Phantom; -import net.minecraft.world.entity.monster.SpellcasterIllager; +import net.minecraft.world.entity.monster.illager.SpellcasterIllager; import net.minecraft.world.entity.player.ChatVisiblity; -import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.entity.projectile.arrow.AbstractArrow; import net.minecraft.world.entity.raid.Raid; -import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.Rarity; -import net.minecraft.world.item.Tiers; +import net.minecraft.world.item.ToolMaterial; import net.minecraft.world.item.component.FireworkExplosion; +import net.minecraft.world.item.equipment.ArmorMaterial; +import net.minecraft.world.item.equipment.ArmorMaterials; +import net.minecraft.world.level.Explosion; import net.minecraft.world.level.GameType; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.biome.Biome; @@ -73,17 +78,20 @@ import net.minecraft.world.level.block.state.properties.BellAttachType; import net.minecraft.world.level.block.state.properties.ChestType; import net.minecraft.world.level.block.state.properties.ComparatorMode; +import net.minecraft.world.level.block.state.properties.CreakingHeartState; import net.minecraft.world.level.block.state.properties.DoorHingeSide; -import net.minecraft.world.level.block.state.properties.DripstoneThickness; import net.minecraft.world.level.block.state.properties.Half; import net.minecraft.world.level.block.state.properties.NoteBlockInstrument; import net.minecraft.world.level.block.state.properties.PistonType; +import net.minecraft.world.level.block.state.properties.PotentSulfurState; import net.minecraft.world.level.block.state.properties.RailShape; import net.minecraft.world.level.block.state.properties.RedstoneSide; import net.minecraft.world.level.block.state.properties.SculkSensorPhase; import net.minecraft.world.level.block.state.properties.SlabType; +import net.minecraft.world.level.block.state.properties.SpeleothemThickness; import net.minecraft.world.level.block.state.properties.StairsShape; import net.minecraft.world.level.block.state.properties.StructureMode; +import net.minecraft.world.level.block.state.properties.TestBlockMode; import net.minecraft.world.level.block.state.properties.Tilt; import net.minecraft.world.level.block.state.properties.WallSide; import net.minecraft.world.level.levelgen.GenerationStep; @@ -94,6 +102,8 @@ import net.minecraft.world.scores.criteria.ObjectiveCriteria; import net.minecraft.world.ticks.TickPriority; import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.data.type.ItemTier; +import org.spongepowered.api.data.type.ItemTiers; import org.spongepowered.api.item.FireworkShape; import org.spongepowered.api.item.FireworkShapes; import org.spongepowered.api.registry.Registry; @@ -102,7 +112,6 @@ import org.spongepowered.api.scoreboard.criteria.Criteria; import org.spongepowered.api.scoreboard.criteria.Criterion; import org.spongepowered.common.accessor.world.entity.boss.enderdragon.phases.EnderDragonPhaseAccessor; -import org.spongepowered.common.accessor.world.level.GameRulesAccessor; import org.spongepowered.common.registry.RegistryLoader; import org.spongepowered.common.registry.SpongeRegistryHolder; @@ -142,7 +151,17 @@ private void loadInstanceRegistries() { map.put(EnderDragonPhase.HOVERING, "hover"); }, phase -> CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, ((EnderDragonPhaseAccessor) phase).accessor$name())); this.holder.createRegistry(RegistryTypes.FIREWORK_SHAPE, VanillaRegistryLoader.fireworkShape()); - this.knownName(RegistryTypes.GAME_RULE, GameRulesAccessor.accessor$GAME_RULE_TYPES().keySet(), rule -> CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rule.getId())); + final var materials = new HashMap(); + final var knownMaterials = new ArmorMaterial[]{ + ArmorMaterials.LEATHER,ArmorMaterials.CHAINMAIL, ArmorMaterials.IRON, ArmorMaterials.GOLD, ArmorMaterials.DIAMOND, + ArmorMaterials.TURTLE_SCUTE,ArmorMaterials.NETHERITE, ArmorMaterials.ARMADILLO_SCUTE, ArmorMaterials.COPPER, + }; + for (final ArmorMaterial armorMaterial : knownMaterials) { + materials.put(armorMaterial, armorMaterial.assetId().identifier().toString()); + } + + this.naming(RegistryTypes.ARMOR_MATERIAL, materials.keySet().toArray(new ArmorMaterial[]{}), materials); + this.holder.createRegistry(RegistryTypes.ITEM_TIER, VanillaRegistryLoader.itemTier()); } private void loadEnumRegistries() { @@ -155,25 +174,24 @@ private void loadEnumRegistries() { map.put(AttributeModifier.Operation.ADD_MULTIPLIED_BASE, "multiply_base"); map.put(AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL, "multiply_total"); }); - this.automaticName(RegistryTypes.BOAT_TYPE, Boat.Type.values()); this.automaticName(RegistryTypes.CHEST_ATTACHMENT_TYPE, ChestType.values()); this.automaticName(RegistryTypes.COLLISION_RULE, Team.CollisionRule.values()); this.automaticName(RegistryTypes.COMPARATOR_MODE, ComparatorMode.values()); this.automaticName(RegistryTypes.DIFFICULTY, Difficulty.values()); this.automaticName(RegistryTypes.DYE_COLOR, DyeColor.values()); this.automaticName(RegistryTypes.DOOR_HINGE, DoorHingeSide.values()); - this.automaticName(RegistryTypes.DRIPSTONE_SEGMENT, DripstoneThickness.values()); + this.automaticName(RegistryTypes.DRIPSTONE_SEGMENT, SpeleothemThickness.values()); + this.automaticName(RegistryTypes.EQUIPMENT_CONDITION, EquipmentSlotGroup.values()); this.automaticName(RegistryTypes.EQUIPMENT_GROUP, EquipmentSlot.Type.values()); this.automaticName(RegistryTypes.EQUIPMENT_TYPE, EquipmentSlot.values()); - this.automaticName(RegistryTypes.FOX_TYPE, Fox.Type.values()); + this.automaticName(RegistryTypes.FOX_TYPE, Fox.Variant.values()); this.automaticName(RegistryTypes.GAME_MODE, GameType.values()); this.automaticName(RegistryTypes.HAND_PREFERENCE, HumanoidArm.values()); this.automaticName(RegistryTypes.HAND_TYPE, InteractionHand.values()); this.automaticName(RegistryTypes.INSTRUMENT_TYPE, NoteBlockInstrument.values()); this.automaticName(RegistryTypes.ITEM_RARITY, Rarity.values()); - this.automaticName(RegistryTypes.ITEM_TIER, Tiers.values()); this.automaticName(RegistryTypes.JIGSAW_BLOCK_ORIENTATION, FrontAndTop.values()); - this.automaticName(RegistryTypes.MOOSHROOM_TYPE, MushroomCow.MushroomType.values()); + this.automaticName(RegistryTypes.MOOSHROOM_TYPE, MushroomCow.Variant.values()); this.automaticName(RegistryTypes.OBJECTIVE_DISPLAY_MODE, ObjectiveCriteria.RenderType.values()); this.automaticName(RegistryTypes.PANDA_GENE, Panda.Gene.values()); this.automaticName(RegistryTypes.PHANTOM_PHASE, Phantom.AttackPhase.values()); @@ -182,6 +200,7 @@ private void loadEnumRegistries() { this.automaticName(RegistryTypes.CHAT_VISIBILITY, ChatVisiblity.values()); this.automaticName(RegistryTypes.PISTON_TYPE, PistonType.values()); this.automaticName(RegistryTypes.PORTION_TYPE, Half.values()); + this.automaticName(RegistryTypes.POTENT_SULFUR_STATE, PotentSulfurState.values()); this.automaticName(RegistryTypes.RAID_STATUS, Raid.RaidStatus.values()); this.automaticName(RegistryTypes.ROTATION, Rotation.values()); this.automaticName(RegistryTypes.RAIL_DIRECTION, RailShape.values()); @@ -196,13 +215,13 @@ private void loadEnumRegistries() { this.automaticName(RegistryTypes.WIRE_ATTACHMENT_TYPE, RedstoneSide.values()); this.automaticName(RegistryTypes.ADVANCEMENT_TYPE, AdvancementType.values()); this.automaticName(RegistryTypes.TROPICAL_FISH_SHAPE, TropicalFish.Pattern.values()); + this.automaticName(RegistryTypes.SALMON_SIZE, Salmon.Variant.values()); this.automaticName(RegistryTypes.HEIGHT_TYPE, Heightmap.Types.values()); this.automaticName(RegistryTypes.ENTITY_CATEGORY, MobCategory.values()); this.automaticName(RegistryTypes.WALL_CONNECTION_STATE, WallSide.values()); this.automaticName(RegistryTypes.GRASS_COLOR_MODIFIER, BiomeSpecialEffects.GrassColorModifier.values()); this.automaticName(RegistryTypes.PRECIPITATION, Biome.Precipitation.values()); this.automaticName(RegistryTypes.TEMPERATURE_MODIFIER, Biome.TemperatureModifier.values()); - this.automaticName(RegistryTypes.CARVING_STEP, GenerationStep.Carving.values()); this.automaticName(RegistryTypes.DECORATION_STEP, GenerationStep.Decoration.values()); this.automaticName(RegistryTypes.PARROT_TYPE, Parrot.Variant.values()); this.automaticName(RegistryTypes.RABBIT_TYPE, Rabbit.Variant.values()); @@ -214,11 +233,15 @@ private void loadEnumRegistries() { this.automaticName(RegistryTypes.ITEM_DISPLAY_TYPE, ItemDisplayContext.values()); this.automaticName(RegistryTypes.BILLBOARD_TYPE, Display.BillboardConstraints.values()); this.automaticName(RegistryTypes.TEXT_ALIGNMENT, Display.TextDisplay.Align.values()); + this.automaticName(RegistryTypes.TEST_BLOCK_MODE, TestBlockMode.values()); this.automaticName(RegistryTypes.LIGHT_TYPE, LightLayer.values()); this.automaticName(RegistryTypes.DISPLAY_SLOT, DisplaySlot.values()); this.automaticName(RegistryTypes.PUSH_REACTION, PushReaction.values()); this.automaticName(RegistryTypes.TRIAL_SPAWNER_STATE, TrialSpawnerState.values()); this.automaticName(RegistryTypes.VAULT_STATE, VaultState.values()); + this.automaticName(RegistryTypes.EXPLOSION_BLOCK_INTERACTION, Explosion.BlockInteraction.values()); + this.automaticName(RegistryTypes.CREAKING_HEART_STATES, CreakingHeartState.values()); + this.automaticName(RegistryTypes.AXOLOTL_VARIANT, Axolotl.Variant.values()); } private static RegistryLoader criterion() { @@ -247,6 +270,17 @@ private static RegistryLoader fireworkShape() { }); } + private static RegistryLoader itemTier() { + return RegistryLoader.of(l -> { + l.add(ItemTiers.DIAMOND, k -> (ItemTier) (Object) ToolMaterial.DIAMOND); + l.add(ItemTiers.GOLD, k -> (ItemTier) (Object) ToolMaterial.GOLD); + l.add(ItemTiers.IRON, k -> (ItemTier) (Object) ToolMaterial.IRON); + l.add(ItemTiers.NETHERITE, k -> (ItemTier) (Object) ToolMaterial.NETHERITE); + l.add(ItemTiers.STONE, k -> (ItemTier) (Object) ToolMaterial.STONE); + l.add(ItemTiers.WOOD, k -> (ItemTier) (Object) ToolMaterial.WOOD); + l.add(ItemTiers.COPPER, k -> (ItemTier) (Object) ToolMaterial.COPPER); + }); + } // The following methods are named for clarity above. @@ -308,7 +342,7 @@ private Registry naming(final RegistryType type, final int values, // To address Vanilla shortcomings, some mods will manually prefix their modid onto values they put into Vanilla registry-like // registrars. We need to account for that possibility if (rawId.contains(":")) { - map.put((ResourceKey) (Object) ResourceLocation.parse(rawId), (A) value.getKey()); + map.put((ResourceKey) (Object) Identifier.parse(rawId), (A) value.getKey()); } else { map.put(ResourceKey.sponge(rawId), (A) value.getKey()); } diff --git a/src/main/java/org/spongepowered/common/registry/provider/DimensionEffectProvider.java b/src/main/java/org/spongepowered/common/registry/provider/DimensionEffectProvider.java index 3f173a268ea..1d2c7d189bf 100644 --- a/src/main/java/org/spongepowered/common/registry/provider/DimensionEffectProvider.java +++ b/src/main/java/org/spongepowered/common/registry/provider/DimensionEffectProvider.java @@ -26,6 +26,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.world.level.dimension.BuiltinDimensionTypes; +import net.minecraft.world.level.dimension.DimensionType; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.world.WorldTypeEffect; @@ -37,27 +38,27 @@ public final class DimensionEffectProvider { public static DimensionEffectProvider INSTANCE = new DimensionEffectProvider(); - private static final SpongeWorldTypeEffect OVERWORLD = new SpongeWorldTypeEffect((ResourceKey) (Object) BuiltinDimensionTypes.OVERWORLD_EFFECTS); - private static final SpongeWorldTypeEffect NETHER = new SpongeWorldTypeEffect((ResourceKey) (Object) BuiltinDimensionTypes.NETHER_EFFECTS); - private static final SpongeWorldTypeEffect END = new SpongeWorldTypeEffect((ResourceKey) (Object) BuiltinDimensionTypes.END_EFFECTS); + private static final SpongeWorldTypeEffect OVERWORLD = new SpongeWorldTypeEffect((ResourceKey) (Object) BuiltinDimensionTypes.OVERWORLD.identifier()); + private static final SpongeWorldTypeEffect NETHER = new SpongeWorldTypeEffect((ResourceKey) (Object) BuiltinDimensionTypes.NETHER.identifier()); + private static final SpongeWorldTypeEffect END = new SpongeWorldTypeEffect((ResourceKey) (Object) BuiltinDimensionTypes.END.identifier()); static { - DimensionEffectProvider.INSTANCE.register(OVERWORLD.key(), OVERWORLD); - DimensionEffectProvider.INSTANCE.register(NETHER.key(), NETHER); - DimensionEffectProvider.INSTANCE.register(END.key(), END); + DimensionEffectProvider.INSTANCE.register(DimensionType.Skybox.OVERWORLD, OVERWORLD); + DimensionEffectProvider.INSTANCE.register(DimensionType.Skybox.NONE, NETHER); + DimensionEffectProvider.INSTANCE.register(DimensionType.Skybox.END, END); } - private final Map mappings; + private final Map mappings; private DimensionEffectProvider() { this.mappings = new Object2ObjectOpenHashMap<>(); } - public @Nullable WorldTypeEffect get(final ResourceKey key) { + public @Nullable WorldTypeEffect get(final DimensionType.Skybox key) { return this.mappings.get(key); } - public void register(final ResourceKey key, final WorldTypeEffect effect) { + public void register(final DimensionType.Skybox key, final WorldTypeEffect effect) { this.mappings.put(key, effect); } } diff --git a/src/main/java/org/spongepowered/common/scheduler/ServerScheduler.java b/src/main/java/org/spongepowered/common/scheduler/ServerScheduler.java index 76f8599ed66..be1b41179ce 100644 --- a/src/main/java/org/spongepowered/common/scheduler/ServerScheduler.java +++ b/src/main/java/org/spongepowered/common/scheduler/ServerScheduler.java @@ -41,7 +41,7 @@ public void tick() { super.tick(); Sponge.server().streamOnlinePlayers().forEach(player -> { - try (final EntityTickContext context = TickPhase.Tick.ENTITY.createPhaseContext(PhaseTracker.SERVER).source(player)) { + try (final EntityTickContext context = TickPhase.Tick.ENTITY.createPhaseContext(PhaseTracker.getServerInstanceExplicitly()).source(player)) { context.buildAndSwitch(); // Detect Changes on PlayerInventories marked as dirty. ((PlayerInventoryBridge) ((net.minecraft.world.entity.player.Player) player).getInventory()).bridge$cleanupDirty(); diff --git a/src/main/java/org/spongepowered/common/scoreboard/SpongeDisplaySlotFactory.java b/src/main/java/org/spongepowered/common/scoreboard/SpongeDisplaySlotFactory.java index 9aed7efe677..e16340cf912 100644 --- a/src/main/java/org/spongepowered/common/scoreboard/SpongeDisplaySlotFactory.java +++ b/src/main/java/org/spongepowered/common/scoreboard/SpongeDisplaySlotFactory.java @@ -25,6 +25,7 @@ package org.spongepowered.common.scoreboard; import net.kyori.adventure.text.format.NamedTextColor; +import net.minecraft.world.scores.TeamColor; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.scoreboard.displayslot.DisplaySlot; import org.spongepowered.common.adventure.SpongeAdventure; @@ -36,8 +37,11 @@ public final class SpongeDisplaySlotFactory implements DisplaySlot.Factory { @Override @NonNull public Optional findByTeamColor(final @NonNull NamedTextColor color) { - var displaySlot = net.minecraft.world.scores.DisplaySlot.teamColorToSlot(SpongeAdventure.asVanilla(color)); - return Optional.ofNullable(displaySlot).map(DisplaySlot.class::cast); + final TeamColor teamColor = SpongeAdventure.asVanillaTeamColor(color); + if (teamColor == null) { + return Optional.empty(); + } + return Optional.of((DisplaySlot) (Object) teamColor.displaySlot()); } } diff --git a/src/main/java/org/spongepowered/common/scoreboard/SpongeScoreFormatFactory.java b/src/main/java/org/spongepowered/common/scoreboard/SpongeScoreFormatFactory.java index 083d6f73c6c..0fd6ad58f7c 100644 --- a/src/main/java/org/spongepowered/common/scoreboard/SpongeScoreFormatFactory.java +++ b/src/main/java/org/spongepowered/common/scoreboard/SpongeScoreFormatFactory.java @@ -41,11 +41,11 @@ public ScoreFormat blank() { @Override public ScoreFormat fixed(final Component component) { - return (ScoreFormat) new FixedFormat(SpongeAdventure.asVanilla(component)); + return (ScoreFormat) (Object) new FixedFormat(SpongeAdventure.asVanilla(component)); } @Override public ScoreFormat styled(final Style style) { - return (ScoreFormat) new StyledFormat(SpongeAdventure.asVanilla(style)); + return (ScoreFormat) (Object) new StyledFormat(SpongeAdventure.asVanilla(style)); } } diff --git a/src/main/java/org/spongepowered/common/serialization/MathCodecs.java b/src/main/java/org/spongepowered/common/serialization/MathCodecs.java index 6cee207e581..de6e2ff66e4 100644 --- a/src/main/java/org/spongepowered/common/serialization/MathCodecs.java +++ b/src/main/java/org/spongepowered/common/serialization/MathCodecs.java @@ -25,7 +25,7 @@ package org.spongepowered.common.serialization; import com.mojang.serialization.Codec; -import net.minecraft.Util; +import net.minecraft.util.Util; import org.spongepowered.math.vector.Vector3i; import java.util.stream.IntStream; diff --git a/src/main/java/org/spongepowered/common/server/PerWorldBorderListener.java b/src/main/java/org/spongepowered/common/server/PerWorldBorderListener.java index b9998a6e979..ed8601ecd7e 100644 --- a/src/main/java/org/spongepowered/common/server/PerWorldBorderListener.java +++ b/src/main/java/org/spongepowered/common/server/PerWorldBorderListener.java @@ -44,36 +44,36 @@ public PerWorldBorderListener(final ServerLevel world) { } @Override - public void onBorderSizeSet(final WorldBorder border, final double newSize) { + public void onSetSize(final WorldBorder border, final double newSize) { this.sendWorldBorderActionPacket(new ClientboundSetBorderSizePacket(border)); } @Override - public void onBorderSizeLerping(final WorldBorder border, final double oldSize, final double newSize, final long time) { + public void onLerpSize(final WorldBorder border, final double oldSize, final double newSize, final long time, final long endTime) { this.sendWorldBorderActionPacket(new ClientboundSetBorderLerpSizePacket(border)); } @Override - public void onBorderCenterSet(final WorldBorder border, final double x, final double z) { + public void onSetCenter(final WorldBorder border, final double x, final double z) { this.sendWorldBorderActionPacket(new ClientboundSetBorderCenterPacket(border)); } @Override - public void onBorderSetWarningTime(final WorldBorder border, final int newTime) { + public void onSetWarningTime(final WorldBorder border, final int newTime) { this.sendWorldBorderActionPacket(new ClientboundSetBorderWarningDelayPacket(border)); } @Override - public void onBorderSetWarningBlocks(final WorldBorder border, final int newDistance) { + public void onSetWarningBlocks(final WorldBorder border, final int newDistance) { this.sendWorldBorderActionPacket(new ClientboundSetBorderWarningDistancePacket(border)); } @Override - public void onBorderSetDamagePerBlock(final WorldBorder border, final double newAmount) { + public void onSetDamagePerBlock(final WorldBorder border, final double newAmount) { } @Override - public void onBorderSetDamageSafeZOne(final WorldBorder border, final double newSize) { + public void onSetSafeZone(final WorldBorder border, final double newSize) { } private void sendWorldBorderActionPacket(final Packet packet) { diff --git a/src/main/java/org/spongepowered/common/server/ServerConsoleSystemSubject.java b/src/main/java/org/spongepowered/common/server/ServerConsoleSystemSubject.java index 41b3c4b16bb..397af351916 100644 --- a/src/main/java/org/spongepowered/common/server/ServerConsoleSystemSubject.java +++ b/src/main/java/org/spongepowered/common/server/ServerConsoleSystemSubject.java @@ -29,6 +29,7 @@ import net.kyori.adventure.text.Component; import net.minecraft.commands.CommandSource; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.permissions.LevelBasedPermissionSet; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; @@ -43,6 +44,7 @@ import java.util.Locale; +@SuppressWarnings("deprecation") public final class ServerConsoleSystemSubject extends SpongeSystemSubject implements CommandSourceProviderBridge, CommandSource, CommandSourceBridge { @Override @@ -58,14 +60,14 @@ public void sendMessage(final @NonNull Identity identity, final @NonNull Compone @Override public CommandSourceStack bridge$getCommandSource(final Cause cause) { return new CommandSourceStack(this, - Vec3.ZERO, - Vec2.ZERO, - SpongeCommon.server().getLevel(Level.OVERWORLD), - 4, - "System Subject", - net.minecraft.network.chat.Component.literal("System Subject"), - SpongeCommon.server(), - null); + Vec3.ZERO, + Vec2.ZERO, + SpongeCommon.server().getLevel(Level.OVERWORLD), + LevelBasedPermissionSet.OWNER, + "System Subject", + net.minecraft.network.chat.Component.literal("System Subject"), + SpongeCommon.server(), + null); } @Override diff --git a/src/main/java/org/spongepowered/common/service/SpongeServiceProvider.java b/src/main/java/org/spongepowered/common/service/SpongeServiceProvider.java index a2c3cd6b01f..ea8f268ea55 100644 --- a/src/main/java/org/spongepowered/common/service/SpongeServiceProvider.java +++ b/src/main/java/org/spongepowered/common/service/SpongeServiceProvider.java @@ -269,14 +269,22 @@ private Registration(final Class clazz, final T object, final PluginContainer protected static final class Service { @NonNull private final Class service; - private final @Nullable Class defaultServiceClass; + private final @Nullable Function defaultServiceProvider; @NonNull private final Function configEntryProvider; public Service(final @NonNull Class service, final @NonNull Function configEntryProvider, final @Nullable Class defaultServiceClass) { this.service = service; - this.defaultServiceClass = defaultServiceClass; + this.defaultServiceProvider = defaultServiceClass != null ? i -> i.getInstance(defaultServiceClass) : null; + this.configEntryProvider = configEntryProvider; + } + + public Service(final @NonNull Class service, + final @NonNull Function configEntryProvider, + final @Nullable Function defaultServiceProvider) { + this.service = service; + this.defaultServiceProvider = defaultServiceProvider; this.configEntryProvider = configEntryProvider; } @@ -289,12 +297,12 @@ public String providePluginId(final ServicesCategory.ServicePluginSubCategory se } public boolean suppliesDefault() { - return this.defaultServiceClass != null; + return this.defaultServiceProvider != null; } public @Nullable T provideDefaultService(final Injector injector) { - if (this.defaultServiceClass != null) { - return injector.getInstance(this.defaultServiceClass); + if (this.defaultServiceProvider != null) { + return this.defaultServiceProvider.apply(injector); } else { return null; } diff --git a/src/main/java/org/spongepowered/common/service/game/pagination/ActivePagination.java b/src/main/java/org/spongepowered/common/service/game/pagination/ActivePagination.java index 40dc2e94aa1..109895c9085 100644 --- a/src/main/java/org/spongepowered/common/service/game/pagination/ActivePagination.java +++ b/src/main/java/org/spongepowered/common/service/game/pagination/ActivePagination.java @@ -25,7 +25,6 @@ package org.spongepowered.common.service.game.pagination; import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.event.ClickEvent; @@ -153,7 +152,7 @@ public void specificPage(final int page) throws CommandException { } for (final Component line : toSend) { - src.sendMessage(Identity.nil(), line); + src.sendMessage(line); } } diff --git a/src/main/java/org/spongepowered/common/service/game/pagination/PaginationCalculator.java b/src/main/java/org/spongepowered/common/service/game/pagination/PaginationCalculator.java index 1936edd3969..7a17d7bd937 100644 --- a/src/main/java/org/spongepowered/common/service/game/pagination/PaginationCalculator.java +++ b/src/main/java/org/spongepowered/common/service/game/pagination/PaginationCalculator.java @@ -27,6 +27,7 @@ import com.google.common.annotations.VisibleForTesting; import net.kyori.adventure.text.BuildableComponent; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.format.Style; @@ -170,16 +171,16 @@ int getWidth(final Component text) { } else if (child instanceof TranslatableComponent) { final TranslatableComponent tl = (TranslatableComponent) child; // Try to find a value registered from Adventure - final MessageFormat global = GlobalTranslator.get().translate(tl.key(), Locales.EN_US); + final MessageFormat global = GlobalTranslator.translator().translate(tl.key(), Locales.EN_US); if (global != null) { i_it = global.toPattern().codePoints().iterator(); - children.addAll(tl.args()); + children.addAll(ComponentLike.asComponents(tl.arguments())); } else { // If there's no adventure translation, then fall back to native final String mc = Language.getInstance().getOrDefault(tl.key()); if (!mc.equals(tl.key())) { // we found a translation, so let's include the with elements - children.addAll(tl.args()); + children.addAll(ComponentLike.asComponents(tl.arguments())); } // Either way, this is the best we'll get for calculating width i_it = mc.codePoints().iterator(); diff --git a/src/main/java/org/spongepowered/common/service/game/pagination/SpongePaginationList.java b/src/main/java/org/spongepowered/common/service/game/pagination/SpongePaginationList.java index 5357f3a8e05..e679be6331c 100644 --- a/src/main/java/org/spongepowered/common/service/game/pagination/SpongePaginationList.java +++ b/src/main/java/org/spongepowered/common/service/game/pagination/SpongePaginationList.java @@ -28,7 +28,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.checkerframework.checker.nullness.qual.Nullable; @@ -142,7 +141,7 @@ public void sendTo(final Audience receiver, final int page) { } catch (final CommandException e) { final Component text = e.componentMessage(); if (text != null) { - receiver.sendMessage(Identity.nil(), text.color(NamedTextColor.RED)); + receiver.sendMessage(text.color(NamedTextColor.RED)); } } } diff --git a/src/main/java/org/spongepowered/common/service/server/SpongeServerScopedServiceProvider.java b/src/main/java/org/spongepowered/common/service/server/SpongeServerScopedServiceProvider.java index 845529e28d6..ad153385623 100644 --- a/src/main/java/org/spongepowered/common/service/server/SpongeServerScopedServiceProvider.java +++ b/src/main/java/org/spongepowered/common/service/server/SpongeServerScopedServiceProvider.java @@ -25,9 +25,9 @@ package org.spongepowered.common.service.server; import com.google.common.collect.ImmutableList; -import com.google.inject.Inject; import com.google.inject.Injector; import io.leangen.geantyref.TypeToken; +import net.minecraft.server.permissions.PermissionLevel; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.Game; import org.spongepowered.api.Server; @@ -52,12 +52,11 @@ public final class SpongeServerScopedServiceProvider extends SpongeServiceProvider implements ServiceProvider.ServerScoped { - private final Server server; + private final PermissionLevel functionsPermissionLevel; - @Inject - public SpongeServerScopedServiceProvider(final Server server, final Game game, final Injector injector) { + public SpongeServerScopedServiceProvider(final Game game, final Injector injector, final PermissionLevel functionsPermissionLevel) { super(game, injector); - this.server = server; + this.functionsPermissionLevel = functionsPermissionLevel; } @Override @@ -74,11 +73,11 @@ protected List> servicesToSelect() { .add(new Service<>( EconomyService.class, servicePluginSubCategory -> servicePluginSubCategory.economyService, - null)) + (Class) null)) .add(new Service<>( PermissionService.class, servicePluginSubCategory -> servicePluginSubCategory.permissionService, - SpongePermissionService.class)) + i -> new SpongePermissionService(this.getGame(), this.functionsPermissionLevel))) .add(new Service<>( WhitelistService.class, servicePluginSubCategory -> servicePluginSubCategory.whitelistService, @@ -89,7 +88,7 @@ protected List> servicesToSelect() { @Override protected AbstractProvideServiceEventImpl createEvent(final PluginContainer container, final Service service) { return new AbstractProvideServiceEventImpl.EngineScopedImpl<>(Cause.of(EventContext.empty(), this.getGame()), - this.getGame(), TypeToken.get(service.getServiceClass()), this.server); + this.getGame(), TypeToken.get(service.getServiceClass()), TypeToken.get(Server.class)); } @Override diff --git a/src/main/java/org/spongepowered/common/service/server/ban/SpongeBanService.java b/src/main/java/org/spongepowered/common/service/server/ban/SpongeBanService.java index d2779bc6e82..44f31b05e82 100644 --- a/src/main/java/org/spongepowered/common/service/server/ban/SpongeBanService.java +++ b/src/main/java/org/spongepowered/common/service/server/ban/SpongeBanService.java @@ -27,6 +27,7 @@ import com.google.inject.Singleton; import net.minecraft.server.players.IpBanList; import net.minecraft.server.players.IpBanListEntry; +import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.StoredUserEntry; import net.minecraft.server.players.UserBanList; import net.minecraft.server.players.UserBanListEntry; @@ -78,8 +79,8 @@ public CompletableFuture> bans() { @SuppressWarnings("unchecked") @Override public CompletableFuture> profileBans() { - final StoredUserListAccessor accessor = - (StoredUserListAccessor) this.getUserBanList(); + final StoredUserListAccessor accessor = + (StoredUserListAccessor) this.getUserBanList(); accessor.invoker$removeExpired(); return CompletableFuture.completedFuture(Collections.unmodifiableCollection(new ArrayList<>((Collection) (Object) accessor.accessor$map().values()))); } @@ -87,7 +88,7 @@ public CompletableFuture> profileBans() { @SuppressWarnings("unchecked") @Override public CompletableFuture> ipBans() { - final StoredUserListAccessor accessor = ((StoredUserListAccessor) this.getIPBanList()); + final StoredUserListAccessor accessor = ((StoredUserListAccessor) this.getIPBanList()); accessor.invoker$removeExpired(); return CompletableFuture.completedFuture(Collections.unmodifiableCollection(new ArrayList<>((Collection) (Object) accessor.accessor$map().values()))); } @@ -95,10 +96,10 @@ public CompletableFuture> ipBans() { @SuppressWarnings("unchecked") @Override public CompletableFuture> find(final GameProfile profile) { - final StoredUserListAccessor accessor = - (StoredUserListAccessor) this.getUserBanList(); + final StoredUserListAccessor accessor = + (StoredUserListAccessor) this.getUserBanList(); accessor.invoker$removeExpired(); - return CompletableFuture.completedFuture(Optional.ofNullable((Ban.Profile) accessor.accessor$map().get(accessor.invoker$getKeyForUser(SpongeGameProfile.toMcProfile(profile))))); + return CompletableFuture.completedFuture(Optional.ofNullable((Ban.Profile) accessor.accessor$map().get(accessor.invoker$getKeyForUser(SpongeGameProfile.toNameAndId(profile))))); } @SuppressWarnings("unchecked") @@ -112,11 +113,11 @@ public CompletableFuture> find(final InetAddress address) { @SuppressWarnings("unchecked") public boolean isBanned(final GameProfile profile) { - final StoredUserListAccessor accessor = - (StoredUserListAccessor) this.getUserBanList(); + final StoredUserListAccessor accessor = + (StoredUserListAccessor) this.getUserBanList(); accessor.invoker$removeExpired(); - return accessor.accessor$map().containsKey(accessor.invoker$getKeyForUser(SpongeGameProfile.toMcProfile(profile))); + return accessor.accessor$map().containsKey(accessor.invoker$getKeyForUser(SpongeGameProfile.toNameAndId(profile))); } @SuppressWarnings("unchecked") @@ -131,8 +132,8 @@ public boolean isBanned(final InetAddress address) { @Override public CompletableFuture pardon(final GameProfile profile) { final CompletableFuture> ban = this.find(profile); - final StoredUserListAccessor accessor = - (StoredUserListAccessor) this.getUserBanList(); + final StoredUserListAccessor accessor = + (StoredUserListAccessor) this.getUserBanList(); accessor.invoker$removeExpired(); return ban.thenCompose(result -> result.map(this::remove).orElse(CompletableFuture.completedFuture(false))); } @@ -156,7 +157,7 @@ public CompletableFuture remove(final Ban ban) { .thenApplyAsync(user -> { Sponge.eventManager().post(SpongeEventFactory.createPardonUserEvent(PhaseTracker.getInstance().currentCause(), (Ban.Profile) ban, user)); - UserListUtil.removeEntry(this.getUserBanList(), SpongeGameProfile.toMcProfile(((Ban.Profile) ban).profile())); + UserListUtil.removeEntry(this.getUserBanList(), SpongeGameProfile.toNameAndId(((Ban.Profile) ban).profile())); return true; }, ((MinecraftServerBridge) SpongeCommon.server()).bridge$spongeMainThreadExecutor()); } else if (ban.type().equals(BanTypes.IP.get())) { diff --git a/src/main/java/org/spongepowered/common/service/server/ban/SpongeIPBanList.java b/src/main/java/org/spongepowered/common/service/server/ban/SpongeIPBanList.java index 6ee8716ff4a..32cb8edf41c 100644 --- a/src/main/java/org/spongepowered/common/service/server/ban/SpongeIPBanList.java +++ b/src/main/java/org/spongepowered/common/service/server/ban/SpongeIPBanList.java @@ -27,6 +27,7 @@ import static org.spongepowered.common.util.NetworkUtil.LOCAL_ADDRESS; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.minecraft.server.notifications.EmptyNotificationService; import net.minecraft.server.players.IpBanList; import net.minecraft.server.players.IpBanListEntry; import org.checkerframework.checker.nullness.qual.Nullable; @@ -44,6 +45,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Optional; /** * Redirects all calls to the {@link BanService}. @@ -51,7 +53,7 @@ public final class SpongeIPBanList extends IpBanList { public SpongeIPBanList(final File file) { - super(file); + super(file, new EmptyNotificationService()); } @Override @@ -75,31 +77,31 @@ protected boolean contains(final String entry) { try { return Sponge.server().serviceProvider().banService().find(InetAddress.getByName(obj)).join() - .map(ban -> { - if (ban instanceof IpBanListEntry) { - return (IpBanListEntry) ban; - } - final LegacyComponentSerializer lcs = LegacyComponentSerializer.legacySection(); - return new IpBanListEntry(BanUtil.addressToBanCompatibleString(ban.address()), - Date.from(ban.creationDate()), - ban.banSource().map(lcs::serialize).orElse(null), - ban.expirationDate().map(Date::from).orElse(null), - ban.reason().map(lcs::serialize).orElse(null)); - }) - .orElse(null); + .map(ban -> { + if (ban instanceof IpBanListEntry) { + return (IpBanListEntry) ban; + } + final LegacyComponentSerializer lcs = LegacyComponentSerializer.legacySection(); + return new IpBanListEntry(BanUtil.addressToBanCompatibleString(ban.address()), + Date.from(ban.creationDate()), + ban.banSource().map(lcs::serialize).orElse(null), + ban.expirationDate().map(Date::from).orElse(null), + ban.reason().map(lcs::serialize).orElse(null)); + }) + .orElse(null); } catch (final UnknownHostException e) { throw new IllegalArgumentException("Error parsing Ban IP address!", e); } } @Override - public void remove(final String entry) { + public boolean remove(final String entry) { if (entry.equals(LOCAL_ADDRESS)) { // Check for single player - return; + return false; } try { - Sponge.server().serviceProvider().banService().pardon(InetAddress.getByName(entry)).join(); + return Sponge.server().serviceProvider().banService().pardon(InetAddress.getByName(entry)).join(); } catch (final UnknownHostException e) { throw new IllegalArgumentException("Error parsing Ban IP address!", e); } @@ -115,8 +117,8 @@ public String[] getUserList() { } @Override - public void add(final IpBanListEntry entry) { - Sponge.server().serviceProvider().banService().add((Ban) entry).join(); + public boolean add(final IpBanListEntry entry) { + return Sponge.server().serviceProvider().banService().add((Ban) entry).thenApply(Optional::isPresent).join(); } @Override @@ -127,7 +129,7 @@ public boolean isEmpty() { /** * @author Minecrell - August 22nd, 2016 * @reason Use InetSocketAddress#getHostString() where possible (instead of - * inspecting SocketAddress#toString()) to support IPv6 addresses + * inspecting SocketAddress#toString()) to support IPv6 addresses */ @Override public String getIpFromAddress(final SocketAddress address) { diff --git a/src/main/java/org/spongepowered/common/service/server/ban/SpongeUserBanList.java b/src/main/java/org/spongepowered/common/service/server/ban/SpongeUserBanList.java index 85e8b0d61f8..e020e63cef9 100644 --- a/src/main/java/org/spongepowered/common/service/server/ban/SpongeUserBanList.java +++ b/src/main/java/org/spongepowered/common/service/server/ban/SpongeUserBanList.java @@ -25,6 +25,8 @@ package org.spongepowered.common.service.server.ban; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.minecraft.server.notifications.EmptyNotificationService; +import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.UserBanList; import net.minecraft.server.players.UserBanListEntry; import org.spongepowered.api.Sponge; @@ -47,23 +49,23 @@ public class SpongeUserBanList extends UserBanList { public SpongeUserBanList(final File file) { - super(file); + super(file, new EmptyNotificationService()); } @Override - protected boolean contains(final com.mojang.authlib.GameProfile profile) { + protected boolean contains(final NameAndId profile) { return Sponge.server().serviceProvider().banService().find(SpongeGameProfile.of(profile)).join().isPresent(); } @Override - public UserBanListEntry get(final com.mojang.authlib.GameProfile profile) { + public UserBanListEntry get(final NameAndId profile) { final Optional ban = Sponge.server().serviceProvider().banService().find(SpongeGameProfile.of(profile)).join(); return ban.map(x -> { if (x instanceof UserBanListEntry) { return (UserBanListEntry) x; } else { final LegacyComponentSerializer lcs = LegacyComponentSerializer.legacySection(); - return new UserBanListEntry(SpongeGameProfile.toMcProfile(x.profile()), + return new UserBanListEntry(SpongeGameProfile.toNameAndId(x.profile()), Date.from(x.creationDate()), x.banSource().map(lcs::serialize).orElse(null), x.expirationDate().map(Date::from).orElse(null), @@ -83,9 +85,10 @@ public String[] getUserList() { } @Override - public void add(final UserBanListEntry entry) { + public boolean add(final UserBanListEntry entry) { final CompletableFuture> completableFuture = Sponge.server().serviceProvider().banService().add((Ban) entry); ((MinecraftServerBridge) SpongeCommon.server()).bridge$spongeMainThreadExecutor().managedBlock(completableFuture::isDone); + return true; } @Override @@ -94,9 +97,10 @@ public boolean isEmpty() { } @Override - public void remove(final com.mojang.authlib.GameProfile entry) { + public boolean remove(final NameAndId entry) { final CompletableFuture completableFuture = Sponge.server().serviceProvider().banService().pardon(SpongeGameProfile.of(entry)); ((MinecraftServerBridge) SpongeCommon.server()).bridge$spongeMainThreadExecutor().managedBlock(completableFuture::isDone); + return true; } } diff --git a/src/main/java/org/spongepowered/common/service/server/permission/OpLevelCollection.java b/src/main/java/org/spongepowered/common/service/server/permission/OpLevelCollection.java index 0994d18d9ba..15b2c5bd086 100644 --- a/src/main/java/org/spongepowered/common/service/server/permission/OpLevelCollection.java +++ b/src/main/java/org/spongepowered/common/service/server/permission/OpLevelCollection.java @@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import net.minecraft.server.permissions.PermissionLevel; import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.Subject; @@ -45,8 +46,8 @@ public class OpLevelCollection extends SpongeSubjectCollection { public OpLevelCollection(final SpongePermissionService service) { super(PermissionService.SUBJECTS_GROUP, service); final ImmutableMap.Builder build = ImmutableMap.builder(); - for (int i = 0; i <= 4; ++i) { - build.put("op_" + i, new OpLevelSubject(service, i)); // TODO: Add subject data + for (var level : PermissionLevel.values()) { + build.put("op_" + level.ordinal(), new OpLevelSubject(service, level)); // TODO: Add subject data } this.levels = build.build(); } @@ -74,10 +75,10 @@ public Collection loadedSubjects() { public static class OpLevelSubject extends SpongeSubject { private final SpongePermissionService service; - private final int level; + private final PermissionLevel level; private final MemorySubjectData data; - public OpLevelSubject(final SpongePermissionService service, final int level) { + public OpLevelSubject(final SpongePermissionService service, final PermissionLevel level) { this.service = service; this.level = level; this.data = new GlobalMemorySubjectData(this) { @@ -87,23 +88,27 @@ public List parents(final Set contexts) { if (!contexts.isEmpty()) { return Collections.emptyList(); } - if (level == 0) { + if (level == PermissionLevel.ALL) { return super.parents(contexts); } else { - return ImmutableList.builder().add(service.getGroupForOpLevel(level - 1).asSubjectReference()).addAll(super.parents(contexts)).build(); + if (level == PermissionLevel.ALL) { + return ImmutableList.builder().add(service.getGroupForOpLevel(level).asSubjectReference()).addAll(super.parents(contexts)).build(); + } + var lesser = PermissionLevel.values()[level.ordinal() - 1]; + return ImmutableList.builder().add(service.getGroupForOpLevel(lesser).asSubjectReference()).addAll(super.parents(contexts)).build(); } } }; SpongePermissions.populateNonCommandPermissions(this.data, (permLevel, name) -> level == permLevel); } - public int opLevel() { + public PermissionLevel opLevel() { return this.level; } @Override public String identifier() { - return "op_" + this.level; + return "op_" + this.level.ordinal(); } @Override diff --git a/src/main/java/org/spongepowered/common/service/server/permission/SpongePermissionService.java b/src/main/java/org/spongepowered/common/service/server/permission/SpongePermissionService.java index 819721fc656..de2b0702280 100644 --- a/src/main/java/org/spongepowered/common/service/server/permission/SpongePermissionService.java +++ b/src/main/java/org/spongepowered/common/service/server/permission/SpongePermissionService.java @@ -27,8 +27,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.inject.Inject; import com.google.inject.Singleton; +import net.minecraft.server.permissions.PermissionLevel; import net.minecraft.server.players.ServerOpList; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.Game; @@ -68,8 +68,7 @@ public final class SpongePermissionService implements PermissionService { private final SpongeSubjectCollection defaultCollection; private final SpongeSubject defaultData; - @Inject - public SpongePermissionService(final Game game) { + public SpongePermissionService(final Game game, final PermissionLevel functionsPermissionLevel) { this.game = game; this.subjects.put(SpongePermissionService.SUBJECTS_DEFAULT, (this.defaultCollection = this.newCollection(SpongePermissionService.SUBJECTS_DEFAULT))); this.subjects.put(PermissionService.SUBJECTS_USER, new UserCollection(this)); @@ -77,11 +76,11 @@ public SpongePermissionService(final Game game) { this.subjects.put(PermissionService.SUBJECTS_COMMAND_BLOCK, new DataFactoryCollection( PermissionService.SUBJECTS_COMMAND_BLOCK, this, - s -> new FixedParentMemorySubjectData(s, this.getGroupForOpLevel(2).asSubjectReference()))); + s -> new FixedParentMemorySubjectData(s, this.getGroupForOpLevel(PermissionLevel.GAMEMASTERS).asSubjectReference()))); this.subjects.put(PermissionService.SUBJECTS_SYSTEM, new DataFactoryCollection( PermissionService.SUBJECTS_SYSTEM, this, - s -> new FixedParentMemorySubjectData(s, this.getGroupForOpLevel(4).asSubjectReference()) + s -> new FixedParentMemorySubjectData(s, this.getGroupForOpLevel(PermissionLevel.ALL).asSubjectReference()) // , s -> { // if (s.equals("Server")) { // return SpongeImpl.game().getServer().getConsole(); @@ -92,6 +91,10 @@ public SpongePermissionService(final Game game) { // } )); + this.subjects.put(PermissionService.SUBJECTS_FUNCTION, new DataFactoryCollection( + PermissionService.SUBJECTS_FUNCTION, this, + s -> new FixedParentMemorySubjectData(s, this.getGroupForOpLevel(functionsPermissionLevel).asSubjectReference()))); + this.defaultData = this.getDefaultCollection().get(SpongePermissionService.SUBJECTS_DEFAULT); } @@ -99,12 +102,12 @@ static ServerOpList getOps() { return SpongeCommon.server().getPlayerList().getOps(); } - static int getServerOpLevel() { - return SpongeCommon.server().getOperatorUserPermissionLevel(); + static PermissionLevel getServerOpLevel() { + return SpongeCommon.server().operatorUserPermissions().level(); } - public Subject getGroupForOpLevel(final int level) { - return this.groupSubjects().get("op_" + level); + public Subject getGroupForOpLevel(final PermissionLevel level) { + return this.groupSubjects().get("op_" + level.ordinal()); } @Override diff --git a/src/main/java/org/spongepowered/common/service/server/permission/SpongePermissions.java b/src/main/java/org/spongepowered/common/service/server/permission/SpongePermissions.java index 2ae631ee52e..5c092b38864 100644 --- a/src/main/java/org/spongepowered/common/service/server/permission/SpongePermissions.java +++ b/src/main/java/org/spongepowered/common/service/server/permission/SpongePermissions.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.service.server.permission; -import org.spongepowered.api.Sponge; +import net.minecraft.server.permissions.PermissionLevel; import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.SubjectData; import org.spongepowered.api.util.Tristate; @@ -42,7 +42,7 @@ public final class SpongePermissions { private SpongePermissions() { } - public static void populateNonCommandPermissions(final SubjectData data, final BiFunction testPermission) { + public static void populateNonCommandPermissions(final SubjectData data, final BiFunction testPermission) { if (testPermission.apply(Constants.Permissions.COMMAND_BLOCK_LEVEL, Constants.Command.COMMAND_BLOCK_COMMAND)) { data.setPermission(SubjectData.GLOBAL_CONTEXT, Constants.Permissions.COMMAND_BLOCK_PERMISSION, Tristate.TRUE); } @@ -57,10 +57,9 @@ public static void populateNonCommandPermissions(final SubjectData data, final B } } - public static void registerPermission(final String permissionNode, final int opLevel) { + public static void registerPermission(final PermissionService service, final String permissionNode, final PermissionLevel opLevel) { if (SpongePermissions.REGISTERED_PERMISSIONS.add(permissionNode)) { - final PermissionService service = Sponge.server().serviceProvider().permissionService(); - if (opLevel == 0) { + if (opLevel == PermissionLevel.ALL) { // register as a default permission service.defaults() .transientSubjectData() diff --git a/src/main/java/org/spongepowered/common/service/server/permission/UserCollection.java b/src/main/java/org/spongepowered/common/service/server/permission/UserCollection.java index 803e57f8ea1..3d9ba6409a3 100644 --- a/src/main/java/org/spongepowered/common/service/server/permission/UserCollection.java +++ b/src/main/java/org/spongepowered/common/service/server/permission/UserCollection.java @@ -25,6 +25,7 @@ package org.spongepowered.common.service.server.permission; import com.mojang.authlib.GameProfile; +import net.minecraft.server.players.NameAndId; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.Sponge; import org.spongepowered.api.service.permission.PermissionService; @@ -73,8 +74,7 @@ public boolean isRegistered(final String identifier) { return false; } // Name doesn't matter when getting entries - final GameProfile profile = new GameProfile(uuid, ""); - return SpongePermissionService.getOps().get(profile) != null; + return SpongePermissionService.getOps().get(new NameAndId(uuid, "")) != null; } private @Nullable UUID identityToUuid(final String identifier) { diff --git a/src/main/java/org/spongepowered/common/service/server/permission/UserSubject.java b/src/main/java/org/spongepowered/common/service/server/permission/UserSubject.java index 62637f1ba03..de5b085e69c 100644 --- a/src/main/java/org/spongepowered/common/service/server/permission/UserSubject.java +++ b/src/main/java/org/spongepowered/common/service/server/permission/UserSubject.java @@ -25,6 +25,9 @@ package org.spongepowered.common.service.server.permission; import com.mojang.authlib.GameProfile; +import net.minecraft.server.permissions.LevelBasedPermissionSet; +import net.minecraft.server.permissions.PermissionLevel; +import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.ServerOpListEntry; import org.spongepowered.api.Sponge; import org.spongepowered.api.event.Cause; @@ -43,11 +46,13 @@ */ public class UserSubject extends SpongeSubject { private final GameProfile player; + private final NameAndId nameAndId; private final MemorySubjectData data; private final UserCollection collection; public UserSubject(final GameProfile player, final UserCollection users) { this.player = Objects.requireNonNull(player); + this.nameAndId = new NameAndId(player); this.data = new SingleParentMemorySubjectData(this) { @Override public SubjectReference parent() { @@ -56,20 +61,20 @@ public SubjectReference parent() { @Override public void setParent(final SubjectReference parent) { - final int opLevel; + final PermissionLevel opLevel; if (parent == null) { - opLevel = 0; + opLevel = PermissionLevel.ALL; } else { if (!(parent.resolve().join() instanceof OpLevelCollection.OpLevelSubject)) { return; } opLevel = ((OpLevelCollection.OpLevelSubject) parent).opLevel(); } - if (opLevel > 0) { + if (opLevel.isEqualOrHigherThan(PermissionLevel.ALL)) { // TODO: Should bypassesPlayerLimit be true or false? - SpongePermissionService.getOps().add(new ServerOpListEntry(player, opLevel, false)); + SpongePermissionService.getOps().add(new ServerOpListEntry(UserSubject.this.nameAndId, LevelBasedPermissionSet.forLevel(opLevel), false)); } else { - SpongePermissionService.getOps().remove(player); + SpongePermissionService.getOps().remove(UserSubject.this.nameAndId); } } }; @@ -78,12 +83,12 @@ public void setParent(final SubjectReference parent) { @Override public String identifier() { - return this.player.getId().toString(); + return this.player.id().toString(); } @Override public Optional friendlyIdentifier() { - final String name = this.player.getName(); + final String name = this.player.name(); return name.isEmpty() ? Optional.empty() : Optional.of(name); } @@ -93,19 +98,19 @@ public Optional associatedObject() { return Optional.empty(); } - return Sponge.server().player(this.player.getId()); + return Sponge.server().player(this.player.id()); } - int getOpLevel() { + PermissionLevel getOpLevel() { Preconditions.checkState(Sponge.isServerAvailable(), "Server is not available!"); // Query op level from server ops list based on player's game profile - final ServerOpListEntry entry = SpongePermissionService.getOps().get(this.player); + final ServerOpListEntry entry = SpongePermissionService.getOps().get(this.nameAndId); if (entry == null) { // Take care of singleplayer commands -- unless an op level is specified, this player follows global rules - return SpongeCommon.server().getPlayerList().isOp(this.player) ? SpongeCommon.server().getOperatorUserPermissionLevel() : 0; + return SpongeCommon.server().getPlayerList().isOp(this.nameAndId) ? SpongeCommon.server().operatorUserPermissions().level() : PermissionLevel.ALL; } else { - return entry.getLevel(); + return entry.permissions().level(); } } @@ -133,7 +138,7 @@ public Tristate permissionValue(final String permission, final Cause cause) { if (ret == Tristate.UNDEFINED) { ret = this.dataPermissionValue(this.collection.getService().defaults().subjectData(), permission); } - if (ret == Tristate.UNDEFINED && this.getOpLevel() >= SpongePermissionService.getServerOpLevel()) { + if (ret == Tristate.UNDEFINED && this.getOpLevel().compareTo(SpongePermissionService.getServerOpLevel()) >= 0) { ret = Tristate.TRUE; } return ret; diff --git a/src/main/java/org/spongepowered/common/service/server/whitelist/SpongeUserWhiteList.java b/src/main/java/org/spongepowered/common/service/server/whitelist/SpongeUserWhiteList.java index 7b450b45d77..7e858440e48 100644 --- a/src/main/java/org/spongepowered/common/service/server/whitelist/SpongeUserWhiteList.java +++ b/src/main/java/org/spongepowered/common/service/server/whitelist/SpongeUserWhiteList.java @@ -24,6 +24,8 @@ */ package org.spongepowered.common.service.server.whitelist; +import net.minecraft.server.notifications.EmptyNotificationService; +import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.UserWhiteList; import net.minecraft.server.players.UserWhiteListEntry; import org.spongepowered.api.Sponge; @@ -42,11 +44,11 @@ public class SpongeUserWhiteList extends UserWhiteList { public SpongeUserWhiteList(final File file) { - super(file); + super(file, new EmptyNotificationService()); } @Override - protected boolean contains(final com.mojang.authlib.GameProfile entry) { + protected boolean contains(final NameAndId entry) { return Sponge.server().serviceProvider().whitelistService().isWhitelisted(SpongeGameProfile.of(entry)).join(); } @@ -61,13 +63,13 @@ public String[] getUserList() { @SuppressWarnings("unchecked") @Override - public void add(final UserWhiteListEntry entry) { - Sponge.server().serviceProvider().whitelistService().addProfile(SpongeGameProfile.of(((StoredUserEntryAccessor) entry).accessor$user())).join(); + public boolean add(final UserWhiteListEntry entry) { + return Sponge.server().serviceProvider().whitelistService().addProfile(SpongeGameProfile.of(((StoredUserEntryAccessor) entry).accessor$user())).join(); } @Override - public void remove(final com.mojang.authlib.GameProfile entry) { - Sponge.server().serviceProvider().whitelistService().removeProfile(SpongeGameProfile.of(entry)).join(); + public boolean remove(final NameAndId entry) { + return Sponge.server().serviceProvider().whitelistService().removeProfile(SpongeGameProfile.of(entry)).join(); } @Override diff --git a/src/main/java/org/spongepowered/common/service/server/whitelist/SpongeWhitelistService.java b/src/main/java/org/spongepowered/common/service/server/whitelist/SpongeWhitelistService.java index 337852b75ca..3a1779c5eb2 100644 --- a/src/main/java/org/spongepowered/common/service/server/whitelist/SpongeWhitelistService.java +++ b/src/main/java/org/spongepowered/common/service/server/whitelist/SpongeWhitelistService.java @@ -25,6 +25,7 @@ package org.spongepowered.common.service.server.whitelist; import com.google.inject.Singleton; +import net.minecraft.server.players.NameAndId; import net.minecraft.server.players.UserWhiteList; import net.minecraft.server.players.UserWhiteListEntry; import org.spongepowered.api.profile.GameProfile; @@ -49,8 +50,8 @@ public CompletableFuture> whitelistedProfiles() { final List profiles = new ArrayList<>(); final UserWhiteList list = SpongeCommon.server().getPlayerList().getWhiteList(); - for (final UserWhiteListEntry entry: ((StoredUserListAccessor) list).accessor$map().values()) { - profiles.add(SpongeGameProfile.of(((StoredUserEntryAccessor) entry).accessor$user())); + for (final UserWhiteListEntry entry: ((StoredUserListAccessor) list).accessor$map().values()) { + profiles.add(SpongeGameProfile.of(((StoredUserEntryAccessor) entry).accessor$user())); } return CompletableFuture.completedFuture(profiles); @@ -59,22 +60,22 @@ public CompletableFuture> whitelistedProfiles() { @SuppressWarnings("unchecked") @Override public CompletableFuture isWhitelisted(final GameProfile profile) { - final StoredUserListAccessor whitelist = (StoredUserListAccessor) SpongeWhitelistService + final StoredUserListAccessor whitelist = (StoredUserListAccessor) SpongeWhitelistService .getWhitelist(); whitelist.invoker$removeExpired(); - return CompletableFuture.completedFuture(whitelist.accessor$map().containsKey(whitelist.invoker$getKeyForUser(SpongeGameProfile.toMcProfile(profile)))); + return CompletableFuture.completedFuture(whitelist.accessor$map().containsKey(whitelist.invoker$getKeyForUser(SpongeGameProfile.toNameAndId(profile)))); } @Override public CompletableFuture addProfile(final GameProfile profile) { - final boolean wasWhitelisted = UserListUtil.addEntry(SpongeWhitelistService.getWhitelist(), new UserWhiteListEntry(SpongeGameProfile.toMcProfile(profile))) != null; + final boolean wasWhitelisted = UserListUtil.addEntry(SpongeWhitelistService.getWhitelist(), new UserWhiteListEntry(SpongeGameProfile.toNameAndId(profile))) != null; return CompletableFuture.completedFuture(wasWhitelisted); } @Override public CompletableFuture removeProfile(final GameProfile profile) { - final boolean wasWhitelisted = UserListUtil.removeEntry(SpongeWhitelistService.getWhitelist(), SpongeGameProfile.toMcProfile(profile)) != null; + final boolean wasWhitelisted = UserListUtil.removeEntry(SpongeWhitelistService.getWhitelist(), SpongeGameProfile.toNameAndId(profile)) != null; return CompletableFuture.completedFuture(wasWhitelisted); } diff --git a/src/main/java/org/spongepowered/common/state/AbstractSpongeStateMatcher.java b/src/main/java/org/spongepowered/common/state/AbstractSpongeStateMatcher.java index a165d98c495..cec8a48f299 100644 --- a/src/main/java/org/spongepowered/common/state/AbstractSpongeStateMatcher.java +++ b/src/main/java/org/spongepowered/common/state/AbstractSpongeStateMatcher.java @@ -62,14 +62,17 @@ public abstract class AbstractSpongeStateMatcher, T protected final boolean isValid(final StateHolder stateHolder) { for (final Map.Entry, Object> entry : this.properties.entrySet()) { final Property property = (Property) entry.getKey(); - final Object value = stateHolder.getValues().get(property); - if (value == null || !value.equals(entry.getValue())) { + if (!stateHolder.hasProperty(property)) { + return false; + } + final Object value = stateHolder.getValue((Property) property); + if (!value.equals(entry.getValue())) { return false; } } for (final StateProperty<@NonNull ?> entry : this.requiredProperties) { final Property property = (Property) entry; - if (stateHolder.getValues().get(property) == null) { + if (!stateHolder.hasProperty(property)) { return false; } } diff --git a/src/main/java/org/spongepowered/common/tag/SpongeDefaultedTag.java b/src/main/java/org/spongepowered/common/tag/SpongeDefaultedTag.java new file mode 100644 index 00000000000..3ac88b6c515 --- /dev/null +++ b/src/main/java/org/spongepowered/common/tag/SpongeDefaultedTag.java @@ -0,0 +1,86 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.tag; + +import net.minecraft.core.MappedRegistry; +import net.minecraft.tags.TagKey; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.api.tag.DefaultedTag; +import org.spongepowered.api.tag.Tag; +import org.spongepowered.common.bridge.tags.TagBridge; + +import java.util.Objects; +import java.util.function.Supplier; + +public final class SpongeDefaultedTag implements DefaultedTag, TagBridge { + + private final Supplier defaultHolder; + private final TagKey vanillaTag; + private final Tag spongeTag; + + public SpongeDefaultedTag(final Tag tag, final Supplier defaultHolder) { + this.defaultHolder = Objects.requireNonNull(defaultHolder, "defaultHolder"); + this.vanillaTag = ((TagBridge) tag).bridge$asVanillaTag(); + this.spongeTag = (Tag) (Object) this.vanillaTag; + } + + @Override + public Supplier defaultHolder() { + return this.defaultHolder; + } + + @Override + public RegistryType registry() { + return this.spongeTag.registry(); + } + + @Override + public DefaultedTag asDefaultedTag(final Supplier holder) { + return this.spongeTag.asDefaultedTag(holder); + } + + @Override + public DefaultedTag asScopedTag() { + return this.spongeTag.asScopedTag(); + } + + @Override + public ResourceKey key() { + return this.spongeTag.key(); + } + + @Override + public TagKey bridge$asVanillaTag() { + return this.vanillaTag; + } + + @SuppressWarnings("unchecked") + @Override + public boolean contains(final T value) { + return ((MappedRegistry) this.defaultHolder.get().registry(this.registry())).wrapAsHolder(value).is(this.vanillaTag); + } +} diff --git a/src/main/java/org/spongepowered/common/tag/SpongeTagFactory.java b/src/main/java/org/spongepowered/common/tag/SpongeTagFactory.java index 66238de5e2f..6b9ea2cb690 100644 --- a/src/main/java/org/spongepowered/common/tag/SpongeTagFactory.java +++ b/src/main/java/org/spongepowered/common/tag/SpongeTagFactory.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.tag; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.tags.TagKey; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.registry.RegistryType; @@ -37,8 +37,8 @@ public class SpongeTagFactory implements Tag.Factory { @SuppressWarnings("unchecked") public Tag of(final RegistryType registryType, final ResourceKey key) { final net.minecraft.resources.ResourceKey> regKey = - ResourceKeyAccessor.invoker$create((ResourceLocation) (Object) registryType.root(), (ResourceLocation) (Object) registryType.location()); + ResourceKeyAccessor.invoker$create((Identifier) (Object) registryType.root(), (Identifier) (Object) registryType.location()); // TagKey.create returns interned tag-keys - return (Tag) (Object) TagKey.create(regKey, ((ResourceLocation) (Object) key)); + return (Tag) (Object) TagKey.create(regKey, ((Identifier) (Object) key)); } } diff --git a/src/main/java/org/spongepowered/common/tag/SpongeTagTemplate.java b/src/main/java/org/spongepowered/common/tag/SpongeTagTemplate.java deleted file mode 100644 index 7f7d0c84d03..00000000000 --- a/src/main/java/org/spongepowered/common/tag/SpongeTagTemplate.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.tag; - -import com.google.gson.JsonObject; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagBuilder; -import net.minecraft.tags.TagFile; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataQuery; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.tag.TagTemplate; -import org.spongepowered.api.tag.Taggable; -import org.spongepowered.common.SpongeCommon; - -import java.io.IOException; -import java.util.Map; - -public record SpongeTagTemplate>( - ResourceKey key, boolean replace, Map elements, - Map subTags, - DataPack> pack) implements TagTemplate { - - @Override - public int contentVersion() { - return 1; - } - - @Override - public DataContainer toContainer() { - final JsonObject jsonObject = SpongeTagTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - final DataContainer container = DataFormats.JSON.get().read(jsonObject.getAsString()); - container.set(DataQuery.of("replace"), this.replace); - return container; - } catch (IOException e) { - SpongeCommon.logger().error("Error reading json serialized by minecraft", e); - return DataContainer.createNew(); - } - } - - public static > JsonObject encode(TagTemplate template, RegistryAccess registryAccess) { - final TagBuilder builder = TagBuilder.create(); - final SpongeTagTemplate spongeTemplate = (SpongeTagTemplate) template; - spongeTemplate.elements.forEach((k, v) -> { - final ResourceLocation location = (ResourceLocation) (Object) k; - // "N/A" is supposed to be the source, but we don't know it, and we're serializing it so it isn't used anyway. (Gone when we serializeToJson) - if (v) { - builder.addElement(location); - } else { - builder.addOptionalElement(location); - } - }); - spongeTemplate.subTags.forEach((k, v) -> { - final ResourceLocation location = (ResourceLocation) (Object) k; - if (v) { - builder.addTag(location); - } else { - builder.addOptionalTag(location); - } - }); - return TagFile.CODEC.encodeStart(JsonOps.INSTANCE, new TagFile(builder.build(), spongeTemplate.replace)).getOrThrow().getAsJsonObject(); - } - -} diff --git a/src/main/java/org/spongepowered/common/tag/SpongeTagTemplateBuilder.java b/src/main/java/org/spongepowered/common/tag/SpongeTagTemplateBuilder.java deleted file mode 100644 index becd5815af9..00000000000 --- a/src/main/java/org/spongepowered/common/tag/SpongeTagTemplateBuilder.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.tag; - -import com.google.common.collect.ImmutableMap; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.registry.RegistryKey; -import org.spongepowered.api.tag.Tag; -import org.spongepowered.api.tag.TagTemplate; -import org.spongepowered.api.tag.Taggable; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -public final class SpongeTagTemplateBuilder> implements TagTemplate.Builder { - - private @Nullable ResourceKey key; - private boolean replace = false; - private final Map values = new HashMap<>(); - private final Map children = new HashMap<>(); - private final DataPack> pack; - - public SpongeTagTemplateBuilder(final DataPack> pack) { - this.pack = pack; - } - - @Override - public TagTemplate.Builder key(final ResourceKey key) { - this.key = key; - return this; - } - - @Override - public TagTemplate.Builder replace(final boolean replace) { - this.replace = replace; - return this; - } - - @Override - public TagTemplate.Builder addChild(final Tag childTag, final boolean required) { - this.children.put(childTag.key(), required); - return this; - } - - @Override - public TagTemplate.Builder addChild(final TagTemplate childTag) throws IllegalArgumentException { - final SpongeTagTemplate spongeTagTemplate = ((SpongeTagTemplate) childTag); - // Circular references aren't allow - causes loading failure. So do a basic sanity check, - // we can't ensure theres no circular references until everything is resolved, but we can check - // 1 level down if we're given a tagtemplate. - if (spongeTagTemplate.subTags().containsKey(this.key)) { - throw new IllegalArgumentException("Recursive tag reference detected! Cannot add a child tag with parent as child! Key: " + this.key); - } - this.children.put(childTag.key(), true); - return this; - } - - @Override - public TagTemplate.Builder addChildren(final Collection> children, final boolean required) { - children.stream().map(Tag::key).forEach(key -> this.children.put(key, required)); - return this; - } - - @Override - public TagTemplate.Builder addChildren(final Map, Boolean> childrenMap) { - childrenMap.forEach((k, v) -> this.children.put(k.key(), v)); - return this; - } - - @Override - public TagTemplate.Builder addValue(final RegistryKey value, final boolean required) throws IllegalArgumentException { - this.values.put(value.location(), required); - return this; - } - - @Override - public TagTemplate.Builder addValues(final Collection> values, final boolean required) throws IllegalArgumentException { - values.forEach(v -> this.values.put(v.location(), required)); - return this; - } - - @Override - public @NonNull SpongeTagTemplate build() { - if (this.key == null) { - throw new IllegalStateException("Key has not been provided yet!"); - } - return new SpongeTagTemplate<>(this.key, - this.replace, - ImmutableMap.copyOf(this.values), - ImmutableMap.copyOf(this.children), - this.pack); - } - - @Override - public TagTemplate.Builder reset() { - this.replace = false; - this.values.clear(); - this.children.clear(); - return this; - } - -} diff --git a/src/main/java/org/spongepowered/common/user/SpongeUserManager.java b/src/main/java/org/spongepowered/common/user/SpongeUserManager.java index 60b7c4e369d..09a24729dd9 100644 --- a/src/main/java/org/spongepowered/common/user/SpongeUserManager.java +++ b/src/main/java/org/spongepowered/common/user/SpongeUserManager.java @@ -112,7 +112,8 @@ public CompletableFuture loadOrCreate(final UUID uuid) { return CompletableFuture.completedFuture(null); } return CompletableFuture.supplyAsync(() -> { - final com.mojang.authlib.GameProfile profile = this.server.getProfileCache().get(uuidToUse) + final com.mojang.authlib.GameProfile profile = this.server.services().nameToIdCache().get(uuidToUse) + .map(nameAndId -> new com.mojang.authlib.GameProfile(nameAndId.id(), nameAndId.name())) .orElseGet(() -> new com.mojang.authlib.GameProfile(uuidToUse, "")); try { this.createUser(profile); @@ -134,7 +135,7 @@ public CompletableFuture> load(final String lastKnownName) { .map(SpongeGameProfile::toMcProfile) .orElse(null); if (mcProfile != null) { - return this.load(mcProfile.getId()); + return this.load(mcProfile.id()); } return CompletableFuture.completedFuture(Optional.empty()); } @@ -233,7 +234,7 @@ private UUID ensureNonEmptyUUID(final UUID uuid) { // public void handlePlayerLogin(final com.mojang.authlib.GameProfile mcProfile) throws IOException { - final @Nullable SpongeUserData currentUser = this.userCache.getIfPresent(mcProfile.getId()); + final @Nullable SpongeUserData currentUser = this.userCache.getIfPresent(mcProfile.id()); if (currentUser != null) { // If currentUser have this then we know that the user has changed. if (this.dirtyUsers.contains(currentUser)) { @@ -246,8 +247,8 @@ public void handlePlayerLogin(final com.mojang.authlib.GameProfile mcProfile) th private void createUser(final com.mojang.authlib.GameProfile profile) throws IOException { final @Nullable SpongeUserData user = SpongeUserData.create(profile); - this.userCache.put(profile.getId(), user); - this.userFileCache.userCreated(profile.getId()); + this.userCache.put(profile.id(), user); + this.userFileCache.userCreated(profile.id()); } public void markDirty(final SpongeUserData user) { diff --git a/src/main/java/org/spongepowered/common/util/BookUtil.java b/src/main/java/org/spongepowered/common/util/BookUtil.java index 8297d48dae6..365c9ec5de5 100644 --- a/src/main/java/org/spongepowered/common/util/BookUtil.java +++ b/src/main/java/org/spongepowered/common/util/BookUtil.java @@ -28,8 +28,8 @@ import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundBundlePacket; -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import net.minecraft.network.protocol.game.ClientboundOpenBookPacket; +import net.minecraft.network.protocol.game.ClientboundSetPlayerInventoryPacket; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Inventory; import org.spongepowered.api.data.Keys; @@ -43,8 +43,6 @@ public final class BookUtil { - public static final int PLAYER_INVENTORY = -2; - public static ClientboundBundlePacket createFakeBookViewPacket(final Player player, final Book book) { final ItemStack item = ItemStack.of(ItemTypes.WRITTEN_BOOK); item.offer(Keys.CUSTOM_NAME, book.title()); @@ -52,21 +50,21 @@ public static ClientboundBundlePacket createFakeBookViewPacket(final Player play item.offer(Keys.PAGES, book.pages()); final Inventory inventory = ((net.minecraft.world.entity.player.Player) player).getInventory(); - final int bookSlot = inventory.selected; - final net.minecraft.world.item.ItemStack oldItem = inventory.getSelected(); + final int bookSlot = inventory.getSelectedSlot(); + final net.minecraft.world.item.ItemStack oldItem = inventory.getSelectedItem(); final List> packets = new ArrayList<>(); // First we need to send a fake a Book ItemStack with the BookView's // contents to the player's hand - packets.add(new ClientboundContainerSetSlotPacket(BookUtil.PLAYER_INVENTORY, 0, bookSlot, ItemStackUtil.toNative(item))); + packets.add(new ClientboundSetPlayerInventoryPacket(bookSlot, ItemStackUtil.toNative(item))); // Next we tell the client to open the Book GUI packets.add(new ClientboundOpenBookPacket(InteractionHand.MAIN_HAND)); // Now we can remove the fake Book since it's contents will have already // been transferred to the GUI - packets.add(new ClientboundContainerSetSlotPacket(BookUtil.PLAYER_INVENTORY, 0, bookSlot, oldItem)); + packets.add(new ClientboundSetPlayerInventoryPacket(bookSlot, oldItem)); return new ClientboundBundlePacket(packets); } diff --git a/src/main/java/org/spongepowered/common/util/Constants.java b/src/main/java/org/spongepowered/common/util/Constants.java index 1df3d75aa73..30bca8b4193 100644 --- a/src/main/java/org/spongepowered/common/util/Constants.java +++ b/src/main/java/org/spongepowered/common/util/Constants.java @@ -31,8 +31,9 @@ import com.google.common.collect.HashBiMap; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.serialization.Codec; import net.minecraft.commands.arguments.CompoundTagArgument; -import net.minecraft.commands.arguments.ResourceLocationArgument; +import net.minecraft.commands.arguments.IdentifierArgument; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.DoubleTag; @@ -40,7 +41,10 @@ import net.minecraft.nbt.ListTag; import net.minecraft.server.level.ChunkLevel; import net.minecraft.server.level.FullChunkStatus; -import net.minecraft.world.inventory.ClickType; +import net.minecraft.server.permissions.PermissionLevel; +import net.minecraft.util.Util; +import net.minecraft.world.entity.Relative; +import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.state.BlockState; @@ -50,8 +54,6 @@ import org.spongepowered.api.data.persistence.DataQuery; import org.spongepowered.api.data.type.ArtType; import org.spongepowered.api.data.type.ArtTypes; -import org.spongepowered.api.data.type.CatType; -import org.spongepowered.api.data.type.CatTypes; import org.spongepowered.api.data.type.ComparatorMode; import org.spongepowered.api.data.type.ComparatorModes; import org.spongepowered.api.data.type.DyeColor; @@ -64,8 +66,6 @@ import org.spongepowered.api.data.type.HorseStyles; import org.spongepowered.api.data.type.LlamaType; import org.spongepowered.api.data.type.LlamaTypes; -import org.spongepowered.api.data.type.ParrotType; -import org.spongepowered.api.data.type.ParrotTypes; import org.spongepowered.api.data.type.RabbitType; import org.spongepowered.api.data.type.RabbitTypes; import org.spongepowered.api.data.type.StructureMode; @@ -74,13 +74,18 @@ import org.spongepowered.api.entity.living.player.gamemode.GameModes; import org.spongepowered.api.util.Axis; import org.spongepowered.api.util.Direction; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3f; import org.spongepowered.math.vector.Vector3i; import java.net.InetSocketAddress; import java.time.Duration; +import java.time.Instant; import java.util.Comparator; import java.util.EnumSet; +import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.function.Supplier; /** @@ -146,9 +151,11 @@ public static final class V2 { * {@link org.spongepowered.api.data.persistence.DataStore} based data */ public static final class V3 { + public static final String SPONGE_TAG_KEY = "sponge-data"; public static final DataQuery SPONGE_DATA_ROOT = DataQuery.of("sponge-data"); public static final DataQuery CONTENT_VERSION = DataQuery.of("version"); public static final DataQuery CONTENT = DataQuery.of("content"); + public static final String SPONGE_WORLD_DATA = "SpongeData"; } } @@ -204,6 +211,7 @@ public static int blockPosToInt(final BlockPos pos) { public static final class PlayerData { public static final DataQuery PLAYER_DATA_JOIN = of("FirstJoin"); + public static final Codec INSTANT_CODEC = Codec.LONG.xmap(Instant::ofEpochMilli, Instant::toEpochMilli); public static final DataQuery PLAYER_DATA_LAST = of("LastPlayed"); } @@ -305,6 +313,7 @@ public static final class World { public static final String UNIQUE_ID = "UUID"; public static final String DIMENSIONS_DIRECTORY = "dimensions"; public static final String DIMENSION = "Dimension"; + public static final String WORLD_KEY = "WorldKey"; } public static final class Schematic { @@ -410,11 +419,11 @@ public static final class Permissions { public static final String SELECTOR_PERMISSION = "minecraft.selector"; public static final String COMMAND_BLOCK_PERMISSION = "minecraft.commandblock"; - public static final int COMMAND_BLOCK_LEVEL = 2; - public static final int SELECTOR_LEVEL = 2; + public static final PermissionLevel COMMAND_BLOCK_LEVEL = PermissionLevel.GAMEMASTERS; + public static final PermissionLevel SELECTOR_LEVEL = PermissionLevel.GAMEMASTERS; public static final String SPONGE_HELP_PERMISSION = "sponge.command.help"; public static final String DEBUG_HOVER_STACKTRACE = "sponge.debug.hover-stacktrace"; - public static final int SPONGE_HELP_LEVEL = 0; + public static final PermissionLevel SPONGE_HELP_LEVEL = PermissionLevel.ALL; } /** @@ -438,7 +447,7 @@ public static final class World { .of(net.minecraft.core.Direction.WEST, net.minecraft.core.Direction.EAST, net.minecraft.core.Direction.DOWN, net.minecraft.core.Direction.UP, net.minecraft.core.Direction.NORTH, net.minecraft.core.Direction.SOUTH); public static final ResourceKey INVALID_WORLD_KEY = ResourceKey.sponge("invalid_world"); - public static final String LEVEL_DAT_OLD = LevelResource.LEVEL_DATA_FILE.getId() + "_old"; + public static final String LEVEL_DAT_OLD = LevelResource.LEVEL_DATA_FILE.id() + "_old"; public static final int DEFAULT_BLOCK_CHANGE_LIMIT = 512; } @@ -490,13 +499,13 @@ public static final class Networking { public static final int CLICK_OUTSIDE_WINDOW = 0x01 << 16 << 1; public static final int CLICK_ANYWHERE = Networking.CLICK_INSIDE_WINDOW | Networking.CLICK_OUTSIDE_WINDOW; // Modes flags - public static final int MODE_CLICK = 0x01 << 9 << ClickType.PICKUP.ordinal(); - public static final int MODE_SHIFT_CLICK = 0x01 << 9 << ClickType.QUICK_MOVE.ordinal(); - public static final int MODE_HOTBAR = 0x01 << 9 << ClickType.SWAP.ordinal(); - public static final int MODE_PICKBLOCK = 0x01 << 9 << ClickType.CLONE.ordinal(); - public static final int MODE_DROP = 0x01 << 9 << ClickType.THROW.ordinal(); - public static final int MODE_DRAG = 0x01 << 9 << ClickType.QUICK_CRAFT.ordinal(); - public static final int MODE_DOUBLE_CLICK = 0x01 << 9 << ClickType.PICKUP_ALL.ordinal(); + public static final int MODE_CLICK = 0x01 << 9 << ContainerInput.PICKUP.ordinal(); + public static final int MODE_SHIFT_CLICK = 0x01 << 9 << ContainerInput.QUICK_MOVE.ordinal(); + public static final int MODE_HOTBAR = 0x01 << 9 << ContainerInput.SWAP.ordinal(); + public static final int MODE_PICKBLOCK = 0x01 << 9 << ContainerInput.CLONE.ordinal(); + public static final int MODE_DROP = 0x01 << 9 << ContainerInput.THROW.ordinal(); + public static final int MODE_DRAG = 0x01 << 9 << ContainerInput.QUICK_CRAFT.ordinal(); + public static final int MODE_DOUBLE_CLICK = 0x01 << 9 << ContainerInput.PICKUP_ALL.ordinal(); // Drag mode flags, bitmasked from button and only set if MODE_DRAG public static final int DRAG_MODE_PRIMARY_BUTTON = 0x01 << 6; // << 0 public static final int DRAG_MODE_SECONDARY_BUTTON = 0x01 << 6 << 1; @@ -755,8 +764,21 @@ public static final class Entity { // These are used by Minecraft's internals for entity spawning public static final String ENTITY_TYPE_ID = "id"; public static final String ENTITY_POSITION = "Pos"; + public static final Codec ENTITY_POSITION_CODEC = Codec.DOUBLE.listOf() + .comapFlatMap( + rawList -> Util.fixedSize(rawList, 3) + .map(list -> new Vector3d(list.get(0), list.get(1), list.get(2))), + vec -> List.of(vec.x(), vec.y(), vec.z()) + ); public static final String ENTITY_DIMENSION = "Dimension"; public static final String ENTITY_ROTATION = "Rotation"; + public static final Codec ROTATIONS_CODEC = Codec.FLOAT + .listOf() + .comapFlatMap( + $$0 -> Util.fixedSize($$0, 2).map($$0x -> new Vector3f($$0x.get(0), $$0x.get(1), 0)), + $$0 -> List.of($$0.x(), $$0.y()) + ); + public static final String ENTITY_UUID = "UUID"; // Entities public static final DataQuery CLASS = of("EntityClass"); @@ -766,6 +788,8 @@ public static final class Entity { public static final DataQuery SCALE = of("Scale"); public static final DataQuery CUSTOM_NAME = of("CustomName"); + public static final Set RELATIVE_POSITION = EnumSet.of(Relative.X, Relative.Y, Relative.Z); + public static final class V2 { public static final DataQuery DATA = of("Data"); @@ -873,20 +897,6 @@ public static final class Minecart { public static final int DEFAULT_FUSE_DURATION = 80; } - public static final class Cat { - - public static final Supplier DEFAULT_TYPE = CatTypes.WHITE; - } - - public static final class Panda { - - public static final int UNHAPPY_TIME = 32; - } - - public static final class Parrot { - - public static final Supplier DEFAULT_TYPE = ParrotTypes.RED_BLUE; - } public static final class Player { @@ -1004,7 +1014,7 @@ public static final class BlockChangeFlags { * Tell the block being changed that it's being moved, rather than being replaced/removed. * The flag, if set, is set as a boolean to {@link BlockState#onPlace(Level, BlockPos, BlockState, boolean)} * by means of calling - * {@link net.minecraft.world.level.chunk.LevelChunk#setBlockState(BlockPos, BlockState, boolean)} + * {@link net.minecraft.world.level.chunk.LevelChunk#setBlockState(BlockPos, BlockState, int)} */ public static final int BLOCK_MOVING = 1 << 6; // 64 /** @@ -1012,9 +1022,9 @@ public static final class BlockChangeFlags { * check if lighting is queued for the block change. */ public static final int LIGHTING_UPDATES = 1 << 7; // 128 if set, blocks lighting updates + public static final int STRUCTURE_PLACEMENT = 1 << 8; // 256 if set, blocks structure placement public static final int PHYSICS_MASK = 1 << 31; // Sponge Added mask, because vanilla doesn't support it yet public static final int PATHFINDING_UPDATES = 1 << 30; // Sponge Added mask, because vanilla doesn't allow bypassing notifications to ai pathfinders - public static final int PERFORM_BLOCK_DESTRUCTION = 1 << 29; // Sponge Added mask, because vanilla doesn't support it yet public static final int MASK = Constants.BlockChangeFlags.BLOCK_UPDATED | Constants.BlockChangeFlags.NOTIFY_CLIENTS | Constants.BlockChangeFlags.IGNORE_RENDER @@ -1025,7 +1035,7 @@ public static final class BlockChangeFlags { | Constants.BlockChangeFlags.LIGHTING_UPDATES | Constants.BlockChangeFlags.PHYSICS_MASK | Constants.BlockChangeFlags.PATHFINDING_UPDATES - | Constants.BlockChangeFlags.PERFORM_BLOCK_DESTRUCTION + | Constants.BlockChangeFlags.STRUCTURE_PLACEMENT ; // All of these flags are what we "expose" to the API // The flags that are naturally inverted are already inverted here by being masked in @@ -1036,7 +1046,7 @@ public static final class BlockChangeFlags { | Constants.BlockChangeFlags.FORCE_RE_RENDER | Constants.BlockChangeFlags.NEIGHBOR_DROPS | Constants.BlockChangeFlags.DENY_NEIGHBOR_SHAPE_UPDATE - | Constants.BlockChangeFlags.PERFORM_BLOCK_DESTRUCTION + | Constants.BlockChangeFlags.STRUCTURE_PLACEMENT ; public static final int DEFAULT = Constants.BlockChangeFlags.BLOCK_UPDATED | Constants.BlockChangeFlags.NOTIFY_CLIENTS; @@ -1045,7 +1055,7 @@ public static final class BlockChangeFlags { | Constants.BlockChangeFlags.NEIGHBOR_DROPS | Constants.BlockChangeFlags.PATHFINDING_UPDATES | Constants.BlockChangeFlags.LIGHTING_UPDATES - | Constants.BlockChangeFlags.PERFORM_BLOCK_DESTRUCTION + | Constants.BlockChangeFlags.STRUCTURE_PLACEMENT ; @@ -1069,22 +1079,22 @@ public static final class NBT { public static final byte TAG_ANY_NUMERIC = 99; public static CompoundTag filterSpongeCustomData(final CompoundTag rootCompound) { - if (rootCompound.contains(Forge.FORGE_DATA, NBT.TAG_COMPOUND)) { - final CompoundTag forgeCompound = rootCompound.getCompound(Forge.FORGE_DATA); - if (forgeCompound.contains(Sponge.Data.V2.SPONGE_DATA, NBT.TAG_COMPOUND)) { + if (rootCompound.contains(Forge.FORGE_DATA)) { + final CompoundTag forgeCompound = rootCompound.getCompoundOrEmpty(Forge.FORGE_DATA); + if (forgeCompound.contains(Sponge.Data.V2.SPONGE_DATA)) { NBT.cleanseInnerCompound(forgeCompound); } if (forgeCompound.isEmpty()) { rootCompound.remove(Forge.FORGE_DATA); } - } else if (rootCompound.contains(Sponge.Data.V2.SPONGE_DATA, NBT.TAG_COMPOUND)) { + } else if (rootCompound.contains(Sponge.Data.V2.SPONGE_DATA)) { NBT.cleanseInnerCompound(rootCompound); } return rootCompound; } private static void cleanseInnerCompound(final CompoundTag compound) { - final CompoundTag inner = compound.getCompound(Sponge.Data.V2.SPONGE_DATA); + final CompoundTag inner = compound.getCompoundOrEmpty(Sponge.Data.V2.SPONGE_DATA); if (inner.isEmpty()) { compound.remove(Sponge.Data.V2.SPONGE_DATA); } @@ -1409,7 +1419,7 @@ public static final class Command { public static final ArgumentType STANDARD_STRING_ARGUMENT_TYPE = StringArgumentType.string(); public static final ArgumentType GREEDY_STRING_ARGUMENT_TYPE = StringArgumentType.greedyString(); public static final ArgumentType NBT_ARGUMENT_TYPE = CompoundTagArgument.compoundTag(); - public static final ResourceLocationArgument RESOURCE_LOCATION_TYPE = ResourceLocationArgument.id(); + public static final IdentifierArgument RESOURCE_LOCATION_TYPE = IdentifierArgument.id(); public static final String COMMAND_BLOCK_COMMAND = ""; public static final String SELECTOR_COMMAND = "@"; public static final String SPONGE_HELP_COMMAND = "sponge:help"; @@ -1584,6 +1594,7 @@ public static final class KeyValueMatcher { public static final class TickConversions { public static final int TICK_DURATION_MS = 50; + public static final int TICKS_PER_SECOND = 1000 / TICK_DURATION_MS; public static final Duration EFFECTIVE_MINIMUM_DURATION = Duration.ofMillis(TickConversions.TICK_DURATION_MS); public static final int MINECRAFT_DAY_TICKS = 24000; diff --git a/src/main/java/org/spongepowered/common/util/DamageEventUtil.java b/src/main/java/org/spongepowered/common/util/DamageEventUtil.java index 8ae17c2f4a0..ac40ff9fc85 100644 --- a/src/main/java/org/spongepowered/common/util/DamageEventUtil.java +++ b/src/main/java/org/spongepowered/common/util/DamageEventUtil.java @@ -25,111 +25,19 @@ package org.spongepowered.common.util; import net.minecraft.core.BlockPos; -import net.minecraft.core.component.DataComponents; -import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; -import net.minecraft.world.damagesource.CombatRules; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.ai.attributes.Attributes; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.enchantment.Enchantment; -import net.minecraft.world.item.enchantment.EnchantmentHelper; -import net.minecraft.world.item.enchantment.ItemEnchantments; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.phys.AABB; -import org.apache.commons.lang3.mutable.MutableFloat; -import org.spongepowered.api.event.Cause; -import org.spongepowered.api.event.CauseStackManager; -import org.spongepowered.api.event.EventContext; -import org.spongepowered.api.event.EventContextKeys; -import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.cause.entity.damage.DamageFunction; -import org.spongepowered.api.event.cause.entity.damage.DamageModifier; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierType; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierTypes; -import org.spongepowered.api.event.entity.AttackEntityEvent; -import org.spongepowered.api.event.entity.DamageEntityEvent; -import org.spongepowered.api.registry.DefaultedRegistryReference; -import org.spongepowered.api.util.Tuple; import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.api.world.server.ServerWorld; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.bridge.CreatorTrackedBridge; -import org.spongepowered.common.bridge.world.damagesource.DamageSourceBridge; -import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge; -import org.spongepowered.common.event.cause.entity.damage.SpongeDamageSources; -import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.item.util.ItemStackUtil; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.function.DoubleUnaryOperator; import java.util.function.Predicate; public final class DamageEventUtil { - private DamageEventUtil() { - } - - - @SuppressWarnings("ConstantConditions") - public static DamageFunction createHardHatModifier(final ItemStack headItem, final float multiplier) { - final var snapshot = ItemStackUtil.snapshotOf(headItem); - final var modifier = DamageEventUtil.buildDamageReductionModifier(DamageModifierTypes.HARD_HAT, snapshot); - return new DamageFunction(modifier, damage -> damage * multiplier); - } - - /** - * LivingEntity#getDamageAfterArmorAbsorb - */ - public static DamageFunction createArmorModifiers(final LivingEntity living, final DamageSource damageSource) { - final DoubleUnaryOperator function = dmg -> CombatRules.getDamageAfterAbsorb(living, (float) dmg, - damageSource, living.getArmorValue(), (float) living.getAttributeValue(Attributes.ARMOR_TOUGHNESS)); - final var modifier = DamageEventUtil.buildDamageReductionModifierWithFrame(DamageModifierTypes.ARMOR, living, Attributes.ARMOR_TOUGHNESS); - - return DamageFunction.of(modifier, function); - } - - /** - * LivingEntity#getDamageAfterMagicAbsorb - */ - public static DamageFunction createResistanceModifier(final LivingEntity living) { - final var effect = living.getEffect(MobEffects.DAMAGE_RESISTANCE); - var modifier = DamageEventUtil.buildDamageReductionModifier(DamageModifierTypes.DEFENSIVE_POTION_EFFECT, effect == null ? living : effect); - return new DamageFunction(modifier, DamageEventUtil.createResistanceFunction(living)); - } - - public static DoubleUnaryOperator createResistanceFunction(final LivingEntity living) { - final var effect = living.getEffect(MobEffects.DAMAGE_RESISTANCE); - final int base = effect == null ? 0 : (effect.getAmplifier() + 1) * 5; - final int modifier = 25 - base; - return damage -> Math.max(((damage * modifier) / 25.0F), 0.0f); - } - - - /** - * LivingEntity#getDamageAfterMagicAbsorb - */ - public static DamageFunction createEnchantmentModifiers(final LivingEntity living, final float damageProtection) { - final DoubleUnaryOperator func = damage -> CombatRules.getDamageAfterMagicAbsorb((float) damage, damageProtection); - final var modifier = DamageEventUtil.buildDamageReductionModifierWithFrame(DamageModifierTypes.ARMOR_ENCHANTMENT, living); - return new DamageFunction(modifier, func); - } - - public static DamageFunction createAbsorptionModifier(final LivingEntity living, final float absorptionAmount) { - final var modifier = DamageEventUtil.buildDamageReductionModifier(DamageModifierTypes.ABSORPTION, living); - return new DamageFunction(modifier, damage -> Math.max(damage - absorptionAmount, 0.0F)); - } - public static ServerLocation findFirstMatchingBlock(final Entity entity, final AABB bb, final Predicate predicate) { final int i = Mth.floor(bb.minX); final int j = Mth.floor(bb.maxX + 1.0D); @@ -156,273 +64,4 @@ public static ServerLocation findFirstMatchingBlock(final Entity entity, final A // Entity is source of fire return ((org.spongepowered.api.entity.Entity) entity).serverLocation(); } - - /** - * This applies various contexts based on the type of {@link DamageSource}, whether - * it's provided by sponge or vanilla. This is not stack neutral, which is why it requires - * a {@link CauseStackManager.StackFrame} reference to push onto the stack. - */ - public static void generateCauseFor(final DamageSource damageSource, final CauseStackManager.StackFrame frame) { - if (damageSource.getDirectEntity() instanceof org.spongepowered.api.entity.Entity entity) { - if (!(entity instanceof Player) && entity instanceof CreatorTrackedBridge creatorBridge) { - creatorBridge.tracker$getCreatorUUID().ifPresent(creator -> frame.addContext(EventContextKeys.CREATOR, creator)); - creatorBridge.tracker$getNotifierUUID().ifPresent(notifier -> frame.addContext(EventContextKeys.NOTIFIER, notifier)); - } - } else if (((DamageSourceBridge) damageSource).bridge$blockLocation() != null) { - final ServerLocation location = ((DamageSourceBridge) damageSource).bridge$blockLocation(); - final BlockPos blockPos = VecHelper.toBlockPos(location); - final LevelChunkBridge chunkBridge = (LevelChunkBridge) ((net.minecraft.world.level.Level) location.world()).getChunkAt(blockPos); - chunkBridge.bridge$getBlockCreatorUUID(blockPos).ifPresent(creator -> frame.addContext(EventContextKeys.CREATOR, creator)); - chunkBridge.bridge$getBlockNotifierUUID(blockPos).ifPresent(notifier -> frame.addContext(EventContextKeys.NOTIFIER, notifier)); - } - frame.pushCause(damageSource); - } - - /** - * Mirrors {@link EnchantmentHelper#modifyDamage} - */ - public static List createAttackEnchantmentFunction(final ItemStack weapon, final Entity entity, final DamageSource damageSource) { - final var enchantments = weapon.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY); - final var snapshot = ItemStackUtil.snapshotOf(weapon); - - return enchantments.entrySet().stream().map(entry -> { - final var enchantment = entry.getKey().value(); - final int level = entry.getIntValue(); - - final var modifier = DamageEventUtil.buildAttackEnchantmentModifier(DamageModifierTypes.WEAPON_ENCHANTMENT, snapshot, enchantment); - - return new DamageFunction(modifier, damage -> - DamageEventUtil.enchantmentDamageFunction(weapon, entity, damageSource, damage, enchantment, level)); - }).toList(); - } - - public static DamageFunction provideSeparateEnchantmentFromBaseDamageFunction(final float baseDamage, final ItemStack weapon) { - final var modifier = DamageEventUtil.buildAttackEnchantmentModifier(DamageModifierTypes.WEAPON_ENCHANTMENT, ItemStackUtil.snapshotOf(weapon)); - return new DamageFunction(modifier, damage -> damage - baseDamage); - } - - public static DamageFunction provideCooldownEnchantmentStrengthFunction(final ItemStack weapon, final float attackStrength) { - final var snapshot = ItemStackUtil.snapshotOf(weapon); - final var modifier = DamageEventUtil.buildAttackEnchantmentModifier(DamageModifierTypes.ATTACK_STRENGTH, snapshot); - return new DamageFunction(modifier, damage -> damage * attackStrength); - } - - private static double enchantmentDamageFunction(final ItemStack weapon, final Entity entity, - final DamageSource damageSource, final double damage, final Enchantment enchantment, final int level) { - var totalDamage = new MutableFloat(damage); - enchantment.modifyDamage((ServerLevel) entity.level(), level, weapon, entity, damageSource, totalDamage); - return totalDamage.doubleValue(); - } - - public static DamageFunction provideCriticalAttackFunction(final Player player, double criticalModifier) { - final var modifier = DamageEventUtil.buildAttackDamageModifier(DamageModifierTypes.CRITICAL_HIT, player); - final DoubleUnaryOperator function = (damage) -> damage * criticalModifier; - return new DamageFunction(modifier, function); - } - - public static DamageFunction provideCooldownAttackStrengthFunction(final Player player, final float attackStrength) { - final var modifier = DamageEventUtil.buildAttackDamageModifier(DamageModifierTypes.ATTACK_COOLDOWN, player); - final DoubleUnaryOperator function = (damage) -> damage * (0.2F + attackStrength * attackStrength * 0.8F); - return new DamageFunction(modifier, function); - } - - public static DamageFunction provideWeaponAttackDamageBonusFunction(final Entity targetEntity, final ItemStack weapon, final DamageSource damageSource) { - final var modifier = DamageEventUtil.buildAttackDamageModifier(DamageModifierTypes.WEAPON_BONUS, targetEntity); - final DoubleUnaryOperator function = (damage) -> damage + weapon.getItem().getAttackDamageBonus(targetEntity, (float) damage, damageSource); - return new DamageFunction(modifier, function); - } - - public static DamageFunction provideSweepingDamageRatioFunction(final ItemStack held, final Player player, final double attackDamage) { - final var modifier = DamageEventUtil.buildAttackEnchantmentModifier(DamageModifierTypes.SWEEPING, ItemStackUtil.snapshotOf(held)); - return DamageFunction.of(modifier, damage -> damage + player.getAttributeValue(Attributes.SWEEPING_DAMAGE_RATIO) * attackDamage); - } - - @SuppressWarnings("ConstantConditions") - public static DamageFunction createShieldFunction(final LivingEntity entity) { - final var snapshot = ItemStackUtil.snapshotOf(entity.getUseItem()); - final var modifier = DamageEventUtil.buildDamageReductionModifier(DamageModifierTypes.SHIELD, entity, snapshot); - return new DamageFunction(modifier, (damage) -> 0); - } - - public static DamageFunction createFreezingBonus(final LivingEntity entity, final DamageSource damageSource, float multiplier) { - final var modifier = DamageEventUtil.buildDamageReductionModifier(DamageModifierTypes.FREEZING_BONUS, damageSource, entity); - return new DamageFunction(modifier, (damage) -> damage * multiplier); - } - - private static DamageModifier buildDamageReductionModifierWithFrame(final DefaultedRegistryReference modifierType, Object... causes) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - for (final Object cause : causes) { - frame.pushCause(cause); - } - return DamageModifier.builder().damageReductionGroup() - .cause(frame.currentCause()).type(modifierType).build(); - } - } - - private static DamageModifier buildAttackDamageModifier(final DefaultedRegistryReference modifierType, Object... causes) { - return DamageModifier.builder().attackDamageGroup() - .cause(Cause.of(EventContext.empty(), Arrays.asList(causes))).type(modifierType).build(); - } - - private static DamageModifier buildAttackEnchantmentModifier(final DefaultedRegistryReference modifierType, Object... causes) { - return DamageModifier.builder().attackEnchantmentGroup() - .cause(Cause.of(EventContext.empty(), Arrays.asList(causes))).type(modifierType).build(); - } - - private static DamageModifier buildDamageReductionModifier(final DefaultedRegistryReference modifierType, Object... causes) { - return DamageModifier.builder().damageReductionGroup() - .cause(Cause.of(EventContext.empty(), Arrays.asList(causes))).type(modifierType).build(); - } - - public static AttackEntityEvent callPlayerAttackEntityEvent(final Attack attack, final float knockbackModifier) { - final boolean isMainthread = !attack.sourceEntity().level().isClientSide; - if (isMainthread) { - PhaseTracker.getInstance().pushCause(attack.dmgSource()); - } - final var currentCause = isMainthread - ? PhaseTracker.getInstance().currentCause() - : Cause.of(EventContext.empty(), attack.dmgSource()); - final var event = attack.postEvent(knockbackModifier, currentCause); - if (isMainthread) { - PhaseTracker.getInstance().popCause(); - } - return event; - } - - /** - * {@link Mob#doHurtTarget} - */ - public static AttackEntityEvent callMobAttackEvent(final Attack attack, final float knockbackModifier) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - frame.pushCause(attack.dmgSource()); - return attack.postEvent(knockbackModifier, frame.currentCause()); - } - } - - /** - * For {@link Entity#hurt} overrides without super call: - * {@link net.minecraft.world.entity.decoration.BlockAttachedEntity#hurt} - * {@link net.minecraft.world.entity.vehicle.VehicleEntity#hurt} - * {@link net.minecraft.world.entity.decoration.ItemFrame#hurt} - * {@link net.minecraft.world.entity.vehicle.MinecartTNT#hurt} - * {@link net.minecraft.world.entity.boss.enderdragon.EndCrystal#hurt} - * {@link net.minecraft.world.entity.projectile.ShulkerBullet#hurt} - * {@link net.minecraft.world.entity.ExperienceOrb#hurt} - * {@link net.minecraft.world.entity.item.ItemEntity#hurt} - */ - public static AttackEntityEvent callOtherAttackEvent( - final Entity targetEntity, - final DamageSource damageSource, - final double damage) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - frame.pushCause(damageSource); - final AttackEntityEvent event = SpongeEventFactory.createAttackEntityEvent(frame.currentCause(), (org.spongepowered.api.entity.Entity) targetEntity, new ArrayList<>(), 0, damage); - SpongeCommon.post(event); - return event; - } - } - - public static DamageEventResult callLivingDamageEntityEvent(final Hurt hurt, final ActuallyHurt actuallyHurt) { - - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - DamageEventUtil.generateCauseFor(actuallyHurt.dmgSource(), frame); - - final List originalFunctions = new ArrayList<>(); - originalFunctions.addAll(hurt.functions()); - originalFunctions.addAll(actuallyHurt.functions()); - final var event = SpongeEventFactory.createDamageEntityEvent(frame.currentCause(), - (org.spongepowered.api.entity.Entity) actuallyHurt.entity(), - originalFunctions, - actuallyHurt.baseDamage()); - - if (actuallyHurt.dmgSource() != SpongeDamageSources.IGNORED) { // Basically, don't throw an event if it's our own damage source - SpongeCommon.post(event); - } - - return new DamageEventResult(event, - actuallyHurt.dmgSource(), - DamageEventUtil.findDamageBefore(event, DamageModifierTypes.SHIELD), - DamageEventUtil.findDamageDifference(event, DamageModifierTypes.SHIELD), - DamageEventUtil.findDamageBefore(event, DamageModifierTypes.HARD_HAT), - DamageEventUtil.findDamageBefore(event, DamageModifierTypes.ARMOR), - DamageEventUtil.findDamageDifference(event, DamageModifierTypes.DEFENSIVE_POTION_EFFECT), // TODO Math.max(0, resisted)? - DamageEventUtil.findDamageDifference(event, DamageModifierTypes.ABSORPTION) - ); - } - - } - - private static Optional findDamageDifference(DamageEntityEvent event, DefaultedRegistryReference type) { - return DamageEventUtil.findModifier(event, type).map(event::damage).map(tuple -> tuple.first() - tuple.second()).map(Double::floatValue); - } - - - private static Optional findDamageBefore(DamageEntityEvent event, DefaultedRegistryReference type) { - return DamageEventUtil.findModifier(event, type).map(event::damage).map(Tuple::first).map(Double::floatValue); - } - - private static Optional findModifier(DamageEntityEvent event, DefaultedRegistryReference type) { - return event.originalFunctions().stream() - .map(DamageFunction::modifier) - .filter(mod -> type.get().equals(mod.type())) - .findFirst(); - } - - - public static DamageEntityEvent callSimpleDamageEntityEvent(final DamageSource source, final Entity targetEntity, final double amount) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { - DamageEventUtil.generateCauseFor(source, frame); - final var event = SpongeEventFactory.createDamageEntityEvent(frame.currentCause(), (org.spongepowered.api.entity.Entity) targetEntity, new ArrayList<>(), amount); - SpongeCommon.post(event); - return event; - } - } - - - - - public record DamageEventResult(DamageEntityEvent event, - DamageSource source, - Optional damageToShield, - Optional damageBlockedByShield, - Optional damageToHelmet, - Optional damageToArmor, - Optional damageResisted, - Optional damageAbsorbed - ) { - - } - - - public record Hurt(DamageSource dmgSource, List functions) { - - } - - public record ActuallyHurt(LivingEntity entity, - List functions, - DamageSource dmgSource, - float baseDamage) { - - } - - public record Attack(T sourceEntity, - Entity target, - ItemStack weapon, - DamageSource dmgSource, - float strengthScale, - float baseDamage, - List functions) { - - private AttackEntityEvent postEvent(final float knockbackModifier, final Cause cause) { - final var event = SpongeEventFactory.createAttackEntityEvent( - cause, - (org.spongepowered.api.entity.Entity) this.target, - this.functions, - knockbackModifier, - this.baseDamage); - SpongeCommon.post(event); - return event; - } - - } } diff --git a/src/main/java/org/spongepowered/common/util/NBTUtil.java b/src/main/java/org/spongepowered/common/util/DataPackUtil.java similarity index 54% rename from src/main/java/org/spongepowered/common/util/NBTUtil.java rename to src/main/java/org/spongepowered/common/util/DataPackUtil.java index 946fb5148f9..d00194f0ee5 100644 --- a/src/main/java/org/spongepowered/common/util/NBTUtil.java +++ b/src/main/java/org/spongepowered/common/util/DataPackUtil.java @@ -24,37 +24,32 @@ */ package org.spongepowered.common.util; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.Tag; -import org.checkerframework.checker.nullness.qual.Nullable; +import com.google.gson.JsonElement; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.JsonOps; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.data.persistence.DataFormats; +import org.spongepowered.api.registry.RegistryHolder; +import org.spongepowered.common.registry.SpongeRegistryHolder; -public final class NBTUtil { +import java.io.IOException; +import java.util.Optional; - public static @Nullable Boolean getNullableBoolean(final CompoundTag compound, final String key) { - final Tag tag = compound.get(key); - if (tag instanceof NumericTag) { - return ((NumericTag) tag).getAsByte() != 0; - } - return null; - } - - public static @Nullable CompoundTag getNullableCompound(final CompoundTag compound, final String key) { - if (!compound.contains(key, Constants.NBT.TAG_COMPOUND)) { - return null; - } - return compound.getCompound(key); - } +public final class DataPackUtil { - public static CompoundTag getOrCreateCompound(final CompoundTag compound, final String key) { - if (!compound.contains(key, Constants.NBT.TAG_COMPOUND)) { - final CompoundTag child = new CompoundTag(); - compound.put(key, child); - return child; + public static Optional toDataContainer(final RegistryHolder registryHolder, final Codec codec, final T value) { + final DataResult result = codec.encodeStart( + ((SpongeRegistryHolder) registryHolder).createSerializationContext(JsonOps.INSTANCE), value); + if (result.isSuccess()) { + try { + return Optional.of(DataFormats.JSON.get().read(result.getOrThrow().toString())); + } catch (final IOException ignored) { + } } - return compound.getCompound(key); + return Optional.empty(); } - private NBTUtil() { + private DataPackUtil() { } } diff --git a/src/main/java/org/spongepowered/common/util/DirectionUtil.java b/src/main/java/org/spongepowered/common/util/DirectionUtil.java index dcbede764bb..d7d8dd9365f 100644 --- a/src/main/java/org/spongepowered/common/util/DirectionUtil.java +++ b/src/main/java/org/spongepowered/common/util/DirectionUtil.java @@ -25,7 +25,7 @@ package org.spongepowered.common.util; -import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; import org.spongepowered.api.util.Direction; import java.util.Objects; @@ -34,45 +34,31 @@ public final class DirectionUtil { public static net.minecraft.core.Direction getFor(final Direction direction) { Objects.requireNonNull(direction); - switch (direction) { - case UP: - return net.minecraft.core.Direction.UP; - case DOWN: - return net.minecraft.core.Direction.DOWN; - case WEST: - return net.minecraft.core.Direction.WEST; - case SOUTH: - return net.minecraft.core.Direction.SOUTH; - case EAST: - return net.minecraft.core.Direction.EAST; - case NORTH: - return net.minecraft.core.Direction.NORTH; - default: - return null; - } + return switch (direction) { + case UP -> net.minecraft.core.Direction.UP; + case DOWN -> net.minecraft.core.Direction.DOWN; + case WEST -> net.minecraft.core.Direction.WEST; + case SOUTH -> net.minecraft.core.Direction.SOUTH; + case EAST -> net.minecraft.core.Direction.EAST; + case NORTH -> net.minecraft.core.Direction.NORTH; + default -> null; + }; } public static Direction getFor(final net.minecraft.core.Direction facing) { Objects.requireNonNull(facing); - switch (facing) { - case UP: - return Direction.UP; - case DOWN: - return Direction.DOWN; - case WEST: - return Direction.WEST; - case SOUTH: - return Direction.SOUTH; - case EAST: - return Direction.EAST; - case NORTH: - return Direction.NORTH; - default: - throw new IllegalStateException(); - } + return switch (facing) { + case UP -> Direction.UP; + case DOWN -> Direction.DOWN; + case WEST -> Direction.WEST; + case SOUTH -> Direction.SOUTH; + case EAST -> Direction.EAST; + case NORTH -> Direction.NORTH; + default -> throw new IllegalStateException(); + }; } - public static net.minecraft.world.level.block.state.BlockState set(final net.minecraft.world.level.block.state.BlockState holder, final Direction value, final DirectionProperty property) { + public static net.minecraft.world.level.block.state.BlockState set(final net.minecraft.world.level.block.state.BlockState holder, final Direction value, final EnumProperty property) { final net.minecraft.core.Direction direction = DirectionUtil.getFor(value); if (direction == null || !property.getPossibleValues().contains(direction)) { return holder; @@ -81,168 +67,101 @@ public static net.minecraft.world.level.block.state.BlockState set(final net.min } public static Direction fromRotation(int i) { - switch (i) { - case 0: - return Direction.SOUTH; - case 1: - return Direction.SOUTH_SOUTHWEST; - case 2: - return Direction.SOUTHWEST; - case 3: - return Direction.WEST_SOUTHWEST; - case 4: - return Direction.WEST; - case 5: - return Direction.WEST_NORTHWEST; - case 6: - return Direction.NORTHWEST; - case 7: - return Direction.NORTH_NORTHWEST; - case 8: - return Direction.NORTH; - case 9: - return Direction.NORTH_NORTHEAST; - case 10: - return Direction.NORTHEAST; - case 11: - return Direction.EAST_NORTHEAST; - case 12: - return Direction.EAST; - case 13: - return Direction.EAST_SOUTHEAST; - case 14: - return Direction.SOUTHEAST; - case 15: - return Direction.SOUTH_SOUTHEAST; - default: - return Direction.NORTH; - } + return switch (i) { + case 0 -> Direction.SOUTH; + case 1 -> Direction.SOUTH_SOUTHWEST; + case 2 -> Direction.SOUTHWEST; + case 3 -> Direction.WEST_SOUTHWEST; + case 4 -> Direction.WEST; + case 5 -> Direction.WEST_NORTHWEST; + case 6 -> Direction.NORTHWEST; + case 7 -> Direction.NORTH_NORTHWEST; + case 8 -> Direction.NORTH; + case 9 -> Direction.NORTH_NORTHEAST; + case 10 -> Direction.NORTHEAST; + case 11 -> Direction.EAST_NORTHEAST; + case 12 -> Direction.EAST; + case 13 -> Direction.EAST_SOUTHEAST; + case 14 -> Direction.SOUTHEAST; + case 15 -> Direction.SOUTH_SOUTHEAST; + default -> Direction.NORTH; + }; } public static int toRotation(final Direction direction) { - switch (direction) { - case SOUTH: - return 0; - case SOUTH_SOUTHWEST: - return 1; - case SOUTHWEST: - return 2; - case WEST_SOUTHWEST: - return 3; - case WEST: - return 4; - case WEST_NORTHWEST: - return 5; - case NORTHWEST: - return 6; - case NORTH_NORTHWEST: - return 7; - case NORTH: - return 8; - case NORTH_NORTHEAST: - return 9; - case NORTHEAST: - return 10; - case EAST_NORTHEAST: - return 11; - case EAST: - return 12; - case EAST_SOUTHEAST: - return 13; - case SOUTHEAST: - return 14; - case SOUTH_SOUTHEAST: - return 15; - default: - return 0; - } + return switch (direction) { + case SOUTH -> 0; + case SOUTH_SOUTHWEST -> 1; + case SOUTHWEST -> 2; + case WEST_SOUTHWEST -> 3; + case WEST -> 4; + case WEST_NORTHWEST -> 5; + case NORTHWEST -> 6; + case NORTH_NORTHWEST -> 7; + case NORTH -> 8; + case NORTH_NORTHEAST -> 9; + case NORTHEAST -> 10; + case EAST_NORTHEAST -> 11; + case EAST -> 12; + case EAST_SOUTHEAST -> 13; + case SOUTHEAST -> 14; + case SOUTH_SOUTHEAST -> 15; + default -> 0; + }; } public static Direction fromHorizontalHanging(int i) { - switch (i) { - case 0: - return Direction.SOUTH; - case 1: - return Direction.WEST; - case 2: - return Direction.NORTH; - case 3: - return Direction.EAST; - default: - return Direction.NORTH; - } + return switch (i) { + case 0 -> Direction.SOUTH; + case 1 -> Direction.WEST; + case 2 -> Direction.NORTH; + case 3 -> Direction.EAST; + default -> Direction.NORTH; + }; } public static int toHorizontalHanging(Direction direction) { - switch (direction) { - case SOUTH: - return 0; - case WEST: - return 1; - case NORTH: - return 2; - case EAST: - return 3; - default: - return 0; - } + return switch (direction) { + case SOUTH -> 0; + case WEST -> 1; + case NORTH -> 2; + case EAST -> 3; + default -> 0; + }; } public static Direction fromHanging(int i) { - switch (i) { - case 0: - return Direction.DOWN; - case 1: - return Direction.UP; - case 2: - return Direction.NORTH; - case 3: - return Direction.SOUTH; - case 4: - return Direction.WEST; - case 5: - return Direction.EAST; - default: - return Direction.DOWN; - } + return switch (i) { + case 0 -> Direction.DOWN; + case 1 -> Direction.UP; + case 2 -> Direction.NORTH; + case 3 -> Direction.SOUTH; + case 4 -> Direction.WEST; + case 5 -> Direction.EAST; + default -> Direction.DOWN; + }; } public static int toHanging(Direction direction) { - switch (direction) { - case DOWN: - return 0; - case UP: - return 1; - case NORTH: - return 2; - case SOUTH: - return 3; - case WEST: - return 4; - case EAST: - return 5; - default: - return 0; - } + return switch (direction) { + case DOWN -> 0; + case UP -> 1; + case NORTH -> 2; + case SOUTH -> 3; + case WEST -> 4; + case EAST -> 5; + default -> 0; + }; } public static int directionToIndex(final Direction direction) { - switch (direction) { - case NORTH: - case NORTHEAST: - case NORTHWEST: - return 0; - case SOUTH: - case SOUTHEAST: - case SOUTHWEST: - return 1; - case EAST: - return 2; - case WEST: - return 3; - default: - throw new IllegalArgumentException("Unexpected direction"); - } + return switch (direction) { + case NORTH, NORTHEAST, NORTHWEST -> 0; + case SOUTH, SOUTHEAST, SOUTHWEST -> 1; + case EAST -> 2; + case WEST -> 3; + default -> throw new IllegalArgumentException("Unexpected direction"); + }; } private DirectionUtil() { diff --git a/src/main/java/org/spongepowered/common/util/DyeColorUtil.java b/src/main/java/org/spongepowered/common/util/DyeColorUtil.java index 8a81546118f..9d932fcdd55 100644 --- a/src/main/java/org/spongepowered/common/util/DyeColorUtil.java +++ b/src/main/java/org/spongepowered/common/util/DyeColorUtil.java @@ -25,40 +25,30 @@ package org.spongepowered.common.util; import net.minecraft.world.item.DyeColor; -import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; -import org.spongepowered.common.accessor.world.entity.animal.SheepAccessor; +import net.minecraft.world.level.block.ColorCollection; import java.util.HashMap; import java.util.Map; -import java.util.stream.Collectors; public class DyeColorUtil { - public static final Map COLOR_BY_WOOL = SheepAccessor.accessor$ITEM_BY_DYE().entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); + public static final Map COLOR_BY_WOOL; - public static Map COLOR_BY_TERRACOTTA = new HashMap<>(); + public static final Map COLOR_BY_TERRACOTTA; static { - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.BLACK_TERRACOTTA, DyeColor.BLACK); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.BLUE_TERRACOTTA, DyeColor.BLUE); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.BROWN_TERRACOTTA, DyeColor.BROWN); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.CYAN_TERRACOTTA, DyeColor.CYAN); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.GRAY_TERRACOTTA, DyeColor.GRAY); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.GREEN_TERRACOTTA, DyeColor.GREEN); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.LIGHT_BLUE_TERRACOTTA, DyeColor.LIGHT_BLUE); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.LIGHT_GRAY_TERRACOTTA, DyeColor.LIGHT_GRAY); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.LIME_TERRACOTTA, DyeColor.LIME); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.MAGENTA_TERRACOTTA, DyeColor.MAGENTA); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.ORANGE_TERRACOTTA, DyeColor.ORANGE); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.PINK_TERRACOTTA, DyeColor.PINK); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.PURPLE_TERRACOTTA, DyeColor.PURPLE); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.RED_TERRACOTTA, DyeColor.RED); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.WHITE_TERRACOTTA, DyeColor.WHITE); - DyeColorUtil.COLOR_BY_TERRACOTTA.put(Blocks.YELLOW_TERRACOTTA, DyeColor.YELLOW); + final Map byWool = new HashMap<>(); + final Map byTerracotta = new HashMap<>(); + final ColorCollection wools = Blocks.WOOL; + final ColorCollection terracottas = Blocks.DYED_TERRACOTTA; + for (final DyeColor color : DyeColor.values()) { + byWool.put(wools.pick(color), color); + byTerracotta.put(terracottas.pick(color), color); + } + COLOR_BY_WOOL = Map.copyOf(byWool); + COLOR_BY_TERRACOTTA = Map.copyOf(byTerracotta); } private DyeColorUtil() { diff --git a/src/main/java/org/spongepowered/common/util/ExecutorUtil.java b/src/main/java/org/spongepowered/common/util/ExecutorUtil.java index eb9e18a628d..d5956f89722 100644 --- a/src/main/java/org/spongepowered/common/util/ExecutorUtil.java +++ b/src/main/java/org/spongepowered/common/util/ExecutorUtil.java @@ -24,9 +24,9 @@ */ package org.spongepowered.common.util; -import net.minecraft.Util; import net.minecraft.server.MinecraftServer; import net.minecraft.util.TimeUtil; +import net.minecraft.util.Util; import org.spongepowered.common.accessor.server.MinecraftServerAccessor; import java.util.concurrent.CompletableFuture; diff --git a/src/main/java/org/spongepowered/common/util/MapUtil.java b/src/main/java/org/spongepowered/common/util/MapUtil.java index 51b029e89bd..62be558c0bc 100644 --- a/src/main/java/org/spongepowered/common/util/MapUtil.java +++ b/src/main/java/org/spongepowered/common/util/MapUtil.java @@ -26,6 +26,7 @@ import com.google.common.primitives.Bytes; import net.minecraft.core.MappedRegistry; +import net.minecraft.core.UUIDUtil; import net.minecraft.nbt.CompoundTag; import org.spongepowered.api.data.persistence.DataQuery; import org.spongepowered.api.data.persistence.DataView; @@ -109,7 +110,7 @@ public static MapShade getMapShadeById(final int id) { public static void saveMapUUIDIndex(final CompoundTag tag, final Map index) { for (final Map.Entry entry : index.entrySet()) { - tag.putUUID(String.valueOf(entry.getKey()), entry.getValue()); + tag.store(String.valueOf(entry.getKey()), UUIDUtil.CODEC, entry.getValue()); } } diff --git a/src/main/java/org/spongepowered/common/util/NBTCollectors.java b/src/main/java/org/spongepowered/common/util/NBTCollectors.java deleted file mode 100644 index 3c8eeda6673..00000000000 --- a/src/main/java/org/spongepowered/common/util/NBTCollectors.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.util; - -import net.minecraft.nbt.ByteTag; -import net.minecraft.nbt.DoubleTag; -import net.minecraft.nbt.FloatTag; -import net.minecraft.nbt.IntTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.LongTag; -import net.minecraft.nbt.ShortTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.nbt.Tag; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collector; - -public final class NBTCollectors { - - private static final Collector TO_TAG_LIST = NBTCollectors.toTagList(value -> value); - private static final Collector TO_LONG_TAG_LIST = NBTCollectors.toTagList(LongTag::valueOf); - private static final Collector TO_INT_TAG_LIST = NBTCollectors.toTagList(IntTag::valueOf); - private static final Collector TO_BYTE_TAG_LIST = NBTCollectors.toTagList(ByteTag::valueOf); - private static final Collector TO_SHORT_TAG_LIST = NBTCollectors.toTagList(ShortTag::valueOf); - private static final Collector TO_BOOLEAN_TAG_LIST = NBTCollectors.toTagList(ByteTag::valueOf); - private static final Collector TO_DOUBLE_TAG_LIST = NBTCollectors.toTagList(DoubleTag::valueOf); - private static final Collector TO_FLOAT_TAG_LIST = NBTCollectors.toTagList(FloatTag::valueOf); - private static final Collector TO_STRING_TAG_LIST = NBTCollectors.toTagList(StringTag::valueOf); - - private static final Collector> TO_STRING_LIST = NBTCollectors.toList(Tag::getAsString); - - private static Collector, List> toList0(final Function toValueFunction) { - return Collector.of(ArrayList::new, - (list, value) -> list.add(toValueFunction.apply(value)), - (first, second) -> { - first.addAll(second); - return first; - }, - list -> list); - } - - public static Collector> toList(final Function toValueFunction) { - return NBTCollectors.toList0(toValueFunction); - } - - public static Collector toTagList(final Function toTagFunction) { - return Collector.of(ListTag::new, - (list, value) -> list.add(toTagFunction.apply(value)), - (first, second) -> { - first.addAll(second); - return first; - }, - list -> list); - } - - public static Collector toTagList() { - return NBTCollectors.TO_TAG_LIST; - } - - public static Collector toBooleanTagList() { - return NBTCollectors.TO_BOOLEAN_TAG_LIST; - } - - public static Collector toByteTagList() { - return NBTCollectors.TO_BYTE_TAG_LIST; - } - - public static Collector toShortTagList() { - return NBTCollectors.TO_SHORT_TAG_LIST; - } - - public static Collector toIntTagList() { - return NBTCollectors.TO_INT_TAG_LIST; - } - - public static Collector toLongTagList() { - return NBTCollectors.TO_LONG_TAG_LIST; - } - - public static Collector toFloatTagList() { - return NBTCollectors.TO_FLOAT_TAG_LIST; - } - - public static Collector toDoubleTagList() { - return NBTCollectors.TO_DOUBLE_TAG_LIST; - } - - public static Collector toStringTagList() { - return NBTCollectors.TO_STRING_TAG_LIST; - } - - public static Collector> toStringList() { - return NBTCollectors.TO_STRING_LIST; - } - - private NBTCollectors() { - } -} diff --git a/src/main/java/org/spongepowered/common/util/NBTStreams.java b/src/main/java/org/spongepowered/common/util/NBTStreams.java deleted file mode 100644 index 062d1e64bea..00000000000 --- a/src/main/java/org/spongepowered/common/util/NBTStreams.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.util; - -import com.google.common.collect.Streams; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.nbt.Tag; - -import java.util.stream.IntStream; -import java.util.stream.LongStream; -import java.util.stream.Stream; - -public final class NBTStreams { - - public static Stream toCompounds(final Iterable iterable) { - return Streams.stream(iterable) - .filter(tag -> tag instanceof CompoundTag) - .map(tag -> (CompoundTag) tag); - } - - public static Stream toStrings(final Iterable iterable) { - return Streams.stream(iterable) - .filter(tag -> tag instanceof StringTag) - .map(Tag::getAsString); - } - - public static LongStream toLongs(final Iterable iterable) { - return Streams.stream(iterable) - .filter(tag -> tag instanceof NumericTag) - .mapToLong(tag -> ((NumericTag) tag).getAsLong()); - } - - public static IntStream toInts(final Iterable iterable) { - return Streams.stream(iterable) - .filter(tag -> tag instanceof NumericTag) - .mapToInt(tag -> ((NumericTag) tag).getAsInt()); - } - - private NBTStreams() { - } -} diff --git a/src/main/java/org/spongepowered/common/util/ReflectionUtil.java b/src/main/java/org/spongepowered/common/util/ReflectionUtil.java index cdcbe61d5ce..012ea23dab5 100644 --- a/src/main/java/org/spongepowered/common/util/ReflectionUtil.java +++ b/src/main/java/org/spongepowered/common/util/ReflectionUtil.java @@ -28,7 +28,6 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import org.apache.logging.log4j.Marker; @@ -47,40 +46,16 @@ public final class ReflectionUtil { public static final Marker REFLECTION_SCANNING = MarkerManager.getMarker("REFLECTION_SCANNING"); - private static final Class[] NEIGHBOR_CHANGED_METHOD_ARGS = { - BlockState.class, - Level.class, - BlockPos.class, - Block.class, - BlockPos.class, - boolean.class - }; private static final Class[] ENTITY_INSIDE_METHOD_ARGS = { BlockState.class, Level.class, BlockPos.class, Entity.class }; - private static final Class[] STEP_ON_METHOD_ARGS = { - Level.class, - BlockPos.class, - BlockState.class, - Entity.class - }; private static final Class[] PLAYER_TOUCH_METHOD_ARGS= { Player.class }; - public static boolean isNeighborChangedDeclared(final Class targetClass) { - return ReflectionUtil.doesMethodExist( - targetClass, - Block.class, - "neighborChanged", - ReflectionUtil.NEIGHBOR_CHANGED_METHOD_ARGS, - void.class - ); - } - public static boolean isEntityInsideDeclared(final Class targetClass) { return ReflectionUtil.doesMethodExist( targetClass, @@ -91,16 +66,6 @@ public static boolean isEntityInsideDeclared(final Class targetClass) { ); } - public static boolean isStepOnDeclared(final Class targetClass) { - return ReflectionUtil.doesMethodExist( - targetClass, - Block.class, - "stepOn", - ReflectionUtil.STEP_ON_METHOD_ARGS, - void.class - ); - } - public static boolean isPlayerTouchDeclared(final Class targetClass) { return ReflectionUtil.doesMethodExist( targetClass, diff --git a/src/main/java/org/spongepowered/common/util/SpongeCriterionUtil.java b/src/main/java/org/spongepowered/common/util/SpongeCriterionUtil.java index 8609d228455..591406b711e 100644 --- a/src/main/java/org/spongepowered/common/util/SpongeCriterionUtil.java +++ b/src/main/java/org/spongepowered/common/util/SpongeCriterionUtil.java @@ -26,7 +26,7 @@ import com.google.common.collect.ImmutableSet; -import net.minecraft.advancements.Criterion; +import net.minecraft.advancements.triggers.Criterion; import org.spongepowered.api.advancement.criteria.AdvancementCriterion; import org.spongepowered.api.advancement.criteria.OperatorCriterion; import org.spongepowered.api.util.Tuple; diff --git a/src/main/java/org/spongepowered/common/util/SpongeTicks.java b/src/main/java/org/spongepowered/common/util/SpongeTicks.java index bfe21863604..d3db1aca012 100644 --- a/src/main/java/org/spongepowered/common/util/SpongeTicks.java +++ b/src/main/java/org/spongepowered/common/util/SpongeTicks.java @@ -109,6 +109,21 @@ public static int toSaturatedIntOrInfinite(final Ticks ticks, final int infinite return (int) ticksValue; } + public static long toSaturatedLongOrInfinite(final Ticks ticks) { + return SpongeTicks.toSaturatedLongOrInfinite(ticks, Constants.TickConversions.INFINITE_TICKS); + } + + public static long toSaturatedLongOrInfinite(final Ticks ticks, final long infiniteTicksMarker) { + if (ticks.isInfinite()) { + return infiniteTicksMarker; + } + long ticksValue = ticks.ticks(); + if (infiniteTicksMarker > 0 && ticksValue >= infiniteTicksMarker) { + ticksValue = infiniteTicksMarker - 1; + } + return ticksValue; + } + public static Ticks ticksOrInfinite(final long ticks) { return SpongeTicks.ticksOrInfinite(ticks, Constants.TickConversions.INFINITE_TICKS); } diff --git a/src/main/java/org/spongepowered/common/util/StateUtil.java b/src/main/java/org/spongepowered/common/util/StateUtil.java index 3b9d0d723e1..81763eb27d5 100644 --- a/src/main/java/org/spongepowered/common/util/StateUtil.java +++ b/src/main/java/org/spongepowered/common/util/StateUtil.java @@ -27,15 +27,15 @@ import net.minecraft.world.level.block.state.StateHolder; import net.minecraft.world.level.block.state.properties.Property; -import java.util.Map; - @SuppressWarnings(value = {"unchecked", "rawtypes"}) public final class StateUtil { public static > S copyStatesFrom(S original, final StateHolder from) { - for (final Map.Entry, Comparable> entry : from.getValues().entrySet()) { - if (original.hasProperty(entry.getKey())) { - original = (S) original.setValue((Property) entry.getKey(), (Comparable) entry.getValue()); + final var values = from.getValues().iterator(); + while (values.hasNext()) { + final Property.Value entry = values.next(); + if (original.hasProperty(entry.property())) { + original = (S) original.setValue((Property) entry.property(), (Comparable) entry.value()); } } return original; diff --git a/src/main/java/org/spongepowered/common/util/TypeTokenUtil.java b/src/main/java/org/spongepowered/common/util/TypeTokenUtil.java index 1dadf6d8ac1..cc7941a26b1 100644 --- a/src/main/java/org/spongepowered/common/util/TypeTokenUtil.java +++ b/src/main/java/org/spongepowered/common/util/TypeTokenUtil.java @@ -56,6 +56,14 @@ public static Type requireCompleteGenerics(final Type input) { return input; } + public static Type[] typeArgumentsFromSupertype(final Type sub, final Class superType) { + final Type calculatedSuper = GenericTypeReflector.getExactSuperType(sub, superType); + if (!(calculatedSuper instanceof ParameterizedType)) { + throw new IllegalArgumentException("Calculated supertype " + calculatedSuper + " of original type " + sub + " is not parameterized"); + } + return ((ParameterizedType) calculatedSuper).getActualTypeArguments(); + } + /** * Given a known declared subtype, determine the value of a specific type parameter in a supertype. * diff --git a/src/main/java/org/spongepowered/common/util/VecHelper.java b/src/main/java/org/spongepowered/common/util/VecHelper.java index 7772d45a2e2..55707a5f1d3 100644 --- a/src/main/java/org/spongepowered/common/util/VecHelper.java +++ b/src/main/java/org/spongepowered/common/util/VecHelper.java @@ -24,6 +24,9 @@ */ package org.spongepowered.common.util; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.core.BlockPos; import net.minecraft.core.Rotations; import net.minecraft.core.Vec3i; @@ -34,10 +37,28 @@ import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.common.world.server.SpongeServerLocation; import org.spongepowered.math.vector.Vector2i; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3f; import org.spongepowered.math.vector.Vector3i; public final class VecHelper { + public static final MapCodec VECTOR3F_CODEC = RecordCodecBuilder.mapCodec(i -> + i.group( + Codec.FLOAT.fieldOf("x").forGetter(Vector3f::x), + Codec.FLOAT.fieldOf("y").forGetter(Vector3f::y), + Codec.FLOAT.fieldOf("z").forGetter(Vector3f::z) + ).apply(i, Vector3f::new) + ); + + public static final MapCodec VECTOR3D_CODEC = RecordCodecBuilder.mapCodec(i -> + i.group( + Codec.DOUBLE.fieldOf("x").forGetter(Vector3d::x), + Codec.DOUBLE.fieldOf("y").forGetter(Vector3d::y), + Codec.DOUBLE.fieldOf("z").forGetter(Vector3d::z) + ).apply(i, Vector3d::new) + ); + // === Flow Vector3d --> BlockPos === public static BlockPos toBlockPos(final org.spongepowered.math.vector.Vector3d vector) { @@ -87,7 +108,7 @@ public static org.spongepowered.math.vector.Vector3d toVector3d(final Rotations if (rotation == null) { return null; } - return new org.spongepowered.math.vector.Vector3d(rotation.getX(), rotation.getY(), rotation.getZ()); + return new org.spongepowered.math.vector.Vector3d(rotation.x(), rotation.y(), rotation.z()); } // === MC Vector3i --> Flow Vector3i === @@ -132,7 +153,7 @@ public static Vector3i toVector3i(final ChunkPos pos) { if (pos == null) { return null; } - return new Vector3i(pos.x, 0, pos.z); + return new Vector3i(pos.x(), 0, pos.z()); } // === Flow Vector3i --> MC ChunkPos === @@ -221,14 +242,12 @@ public static AABB toSpongeAABB(final net.minecraft.world.phys.AABB box) { public static CompoundTag toCompound(final org.spongepowered.math.vector.Vector3d vector) { final CompoundTag compound = new CompoundTag(); - compound.putDouble("x", vector.x()); - compound.putDouble("y", vector.y()); - compound.putDouble("z", vector.z()); + compound.store(VECTOR3D_CODEC, vector); return compound; } public static org.spongepowered.math.vector.Vector3d fromCompound(final CompoundTag compound) { - return new org.spongepowered.math.vector.Vector3d(compound.getDouble("x"), compound.getDouble("y"), compound.getDouble("z")); + return compound.read(VECTOR3D_CODEC).orElse(Vector3d.ZERO); } private VecHelper() { diff --git a/src/main/java/org/spongepowered/common/world/SpongeBlockChangeFlag.java b/src/main/java/org/spongepowered/common/world/SpongeBlockChangeFlag.java index f4968fdd17a..ef801b5f272 100644 --- a/src/main/java/org/spongepowered/common/world/SpongeBlockChangeFlag.java +++ b/src/main/java/org/spongepowered/common/world/SpongeBlockChangeFlag.java @@ -89,7 +89,7 @@ public boolean neighborDropsAllowed() { @Override public boolean performBlockDestruction() { - return (this.rawFlag & Constants.BlockChangeFlags.PERFORM_BLOCK_DESTRUCTION) == 0; // sponge + return (this.rawFlag & Constants.BlockChangeFlags.STRUCTURE_PLACEMENT) == 0; // 256 } @Override @@ -198,8 +198,8 @@ public SpongeBlockChangeFlag withPerformBlockDestruction(boolean performBlockDes return this; } return BlockChangeFlagManager.fromNativeInt(performBlockDestruction - ? this.rawFlag & ~Constants.BlockChangeFlags.PERFORM_BLOCK_DESTRUCTION - : this.rawFlag | Constants.BlockChangeFlags.PERFORM_BLOCK_DESTRUCTION); + ? this.rawFlag & ~Constants.BlockChangeFlags.STRUCTURE_PLACEMENT + : this.rawFlag | Constants.BlockChangeFlags.STRUCTURE_PLACEMENT); } @Override diff --git a/src/main/java/org/spongepowered/common/world/SpongeDefaultWorldKeysFactory.java b/src/main/java/org/spongepowered/common/world/SpongeDefaultWorldKeysFactory.java index 10761e0865a..c47fbcb8d6e 100644 --- a/src/main/java/org/spongepowered/common/world/SpongeDefaultWorldKeysFactory.java +++ b/src/main/java/org/spongepowered/common/world/SpongeDefaultWorldKeysFactory.java @@ -32,16 +32,16 @@ public final class SpongeDefaultWorldKeysFactory implements DefaultWorldKeys.Fac @Override public ResourceKey defaultWorld() { - return (ResourceKey) (Object) Level.OVERWORLD.location(); + return (ResourceKey) (Object) Level.OVERWORLD.identifier(); } @Override public ResourceKey theNether() { - return (ResourceKey) (Object) Level.NETHER.location(); + return (ResourceKey) (Object) Level.NETHER.identifier(); } @Override public ResourceKey theEnd() { - return (ResourceKey) (Object) Level.END.location(); + return (ResourceKey) (Object) Level.END.identifier(); } } diff --git a/src/main/java/org/spongepowered/common/world/SpongeExplosionBuilder.java b/src/main/java/org/spongepowered/common/world/SpongeExplosionBuilder.java index 52a6f8486aa..b1ebdd6ef6b 100644 --- a/src/main/java/org/spongepowered/common/world/SpongeExplosionBuilder.java +++ b/src/main/java/org/spongepowered/common/world/SpongeExplosionBuilder.java @@ -24,18 +24,18 @@ */ package org.spongepowered.common.world; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.sounds.SoundEvents; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Explosion.BlockInteraction; -import net.minecraft.world.level.Level; +import net.minecraft.world.level.ServerExplosion; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.entity.explosive.Explosive; import org.spongepowered.api.world.World; import org.spongepowered.api.world.explosion.Explosion; +import org.spongepowered.api.world.explosion.ExplosionBlockInteraction; import org.spongepowered.api.world.server.ServerLocation; import org.spongepowered.common.bridge.world.level.ExplosionBridge; import org.spongepowered.common.util.Preconditions; +import org.spongepowered.common.util.VecHelper; import org.spongepowered.math.vector.Vector3d; import java.util.Objects; @@ -46,13 +46,14 @@ public class SpongeExplosionBuilder implements Explosion.Builder { @Nullable private Explosive sourceExplosive; private float radius; private boolean canCauseFire; - private boolean shouldBreakBlocks = true; + private net.minecraft.world.level.Explosion.BlockInteraction blockInteraction; private boolean shouldSmoke; private boolean shouldDamageEntities = true; private int resolution = 16; private float randomness = 1.0F; private double knockback = 1; + public SpongeExplosionBuilder() { this.reset(); } @@ -94,11 +95,12 @@ public Explosion.Builder shouldPlaySmoke(boolean smoke) { } @Override - public Explosion.Builder shouldBreakBlocks(boolean destroy) { - this.shouldBreakBlocks = destroy; + public Explosion.Builder blockInteraction(final ExplosionBlockInteraction interaction) { + this.blockInteraction = (net.minecraft.world.level.Explosion.BlockInteraction) (Object) interaction; return this; } + @Override public Explosion.Builder resolution(int resolution) { //A value of 1 would cause a DivideByZeroException @@ -124,7 +126,7 @@ public Explosion.Builder from(Explosion value) { this.sourceExplosive = value.sourceExplosive().orElse(null); this.radius = value.radius(); this.canCauseFire = value.canCauseFire(); - this.shouldBreakBlocks = value.shouldBreakBlocks(); + this.blockInteraction = ((ServerExplosion) value).getBlockInteraction(); this.shouldSmoke = value.shouldPlaySmoke(); this.shouldDamageEntities = value.shouldDamageEntities(); this.resolution = value.resolution(); @@ -136,10 +138,10 @@ public Explosion.Builder from(Explosion value) { @Override public SpongeExplosionBuilder reset() { this.location = null; + this.blockInteraction = null; this.sourceExplosive = null; this.radius = 0; this.canCauseFire = false; - this.shouldBreakBlocks = true; this.shouldSmoke = false; this.shouldDamageEntities = true; this.resolution = 16; @@ -155,14 +157,9 @@ public Explosion build() throws IllegalStateException { final World world = this.location.world(); final Vector3d origin = this.location.position(); - final net.minecraft.world.level.Explosion explosion = new net.minecraft.world.level.Explosion((Level) world, - (Entity) this.sourceExplosive, null, null, origin.x(), origin.y(), origin.z(), this.radius, - this.canCauseFire, this.shouldBreakBlocks ? BlockInteraction.DESTROY : BlockInteraction.KEEP, - // TODO configurable explosion particles & sound? - ParticleTypes.EXPLOSION, - ParticleTypes.EXPLOSION_EMITTER, - SoundEvents.GENERIC_EXPLODE); - ((ExplosionBridge) explosion).bridge$setShouldBreakBlocks(this.shouldBreakBlocks); + final var explosion = new net.minecraft.world.level.ServerExplosion((ServerLevel) world, + (Entity) this.sourceExplosive, null, null, VecHelper.toVanillaVector3d(origin), this.radius, + this.canCauseFire, this.blockInteraction); ((ExplosionBridge) explosion).bridge$setShouldDamageEntities(this.shouldDamageEntities); ((ExplosionBridge) explosion).bridge$setShouldPlaySmoke(this.shouldSmoke); ((ExplosionBridge) explosion).bridge$setResolution(this.resolution); diff --git a/src/main/java/org/spongepowered/common/world/SpongePositionSourceFactory.java b/src/main/java/org/spongepowered/common/world/SpongePositionSourceFactory.java index 338497401d6..c3eb1cc05b0 100644 --- a/src/main/java/org/spongepowered/common/world/SpongePositionSourceFactory.java +++ b/src/main/java/org/spongepowered/common/world/SpongePositionSourceFactory.java @@ -43,7 +43,7 @@ public PositionSource of(final Vector3i position) { @Override public PositionSource of(final int x, final int y, final int z) { - return (PositionSource) new BlockPositionSource(new BlockPos(x, y, z)); + return (PositionSource) (Object) new BlockPositionSource(new BlockPos(x, y, z)); } @Override diff --git a/src/main/java/org/spongepowered/common/world/SpongeWorldTypeEffect.java b/src/main/java/org/spongepowered/common/world/SpongeWorldTypeEffect.java index 9f0193fafe2..8c77a994a16 100644 --- a/src/main/java/org/spongepowered/common/world/SpongeWorldTypeEffect.java +++ b/src/main/java/org/spongepowered/common/world/SpongeWorldTypeEffect.java @@ -24,7 +24,7 @@ */ package org.spongepowered.common.world; -import net.minecraft.world.level.dimension.BuiltinDimensionTypes; +import net.minecraft.world.level.dimension.DimensionType; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.world.WorldTypeEffect; import org.spongepowered.common.AbstractResourceKeyed; @@ -49,17 +49,17 @@ public static final class FactoryImpl implements WorldTypeEffect.Factory { @Override public WorldTypeEffect overworld() { - return DimensionEffectProvider.INSTANCE.get((ResourceKey) (Object) BuiltinDimensionTypes.OVERWORLD_EFFECTS); + return DimensionEffectProvider.INSTANCE.get(DimensionType.Skybox.OVERWORLD); } @Override public WorldTypeEffect nether() { - return DimensionEffectProvider.INSTANCE.get((ResourceKey) (Object) BuiltinDimensionTypes.NETHER_EFFECTS); + return DimensionEffectProvider.INSTANCE.get(DimensionType.Skybox.NONE); } @Override public WorldTypeEffect end() { - return DimensionEffectProvider.INSTANCE.get((ResourceKey) (Object) BuiltinDimensionTypes.END_EFFECTS); + return DimensionEffectProvider.INSTANCE.get(DimensionType.Skybox.END); } } } diff --git a/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeAttributesFactory.java b/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeAttributesFactory.java index 930a81c26e3..63036a76f74 100644 --- a/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeAttributesFactory.java +++ b/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeAttributesFactory.java @@ -85,13 +85,13 @@ public Optional defaultAttributes(final RegistryReference>> list = ImmutableList.builder(); ((OverworldBiomeBuilderAccessor) (Object) new OverworldBiomeBuilder()).accessor$addBiomes(list::add); for (final var pair : list.build()) { - DEFAULT_ATTRIBUTES.put((ResourceKey) (Object) pair.getSecond().location(), (BiomeAttributes) (Object) pair.getFirst()); + DEFAULT_ATTRIBUTES.put((ResourceKey) (Object) pair.getSecond().identifier(), (BiomeAttributes) (Object) pair.getFirst()); } // MultiNoiseBiomeSource.Preset#NETHER - DEFAULT_ATTRIBUTES.put((ResourceKey) (Object) Biomes.SOUL_SAND_VALLEY.location(), (BiomeAttributes) (Object) Climate.parameters(0.0F, -0.5F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F)); - DEFAULT_ATTRIBUTES.put((ResourceKey) (Object) Biomes.CRIMSON_FOREST.location(), (BiomeAttributes) (Object) Climate.parameters(0.4F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F)); - DEFAULT_ATTRIBUTES.put((ResourceKey) (Object) Biomes.WARPED_FOREST.location(), (BiomeAttributes) (Object) Climate.parameters(0.0F, 0.5F, 0.0F, 0.0F, 0.0F, 0.0F, 0.375F)); - DEFAULT_ATTRIBUTES.put((ResourceKey) (Object) Biomes.BASALT_DELTAS.location(), (BiomeAttributes) (Object) Climate.parameters(-0.5F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.175F)); + DEFAULT_ATTRIBUTES.put((ResourceKey) (Object) Biomes.SOUL_SAND_VALLEY.identifier(), (BiomeAttributes) (Object) Climate.parameters(0.0F, -0.5F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F)); + DEFAULT_ATTRIBUTES.put((ResourceKey) (Object) Biomes.CRIMSON_FOREST.identifier(), (BiomeAttributes) (Object) Climate.parameters(0.4F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F)); + DEFAULT_ATTRIBUTES.put((ResourceKey) (Object) Biomes.WARPED_FOREST.identifier(), (BiomeAttributes) (Object) Climate.parameters(0.0F, 0.5F, 0.0F, 0.0F, 0.0F, 0.0F, 0.375F)); + DEFAULT_ATTRIBUTES.put((ResourceKey) (Object) Biomes.BASALT_DELTAS.identifier(), (BiomeAttributes) (Object) Climate.parameters(-0.5F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.175F)); } return Optional.ofNullable(SpongeBiomeAttributesFactory.DEFAULT_ATTRIBUTES.get(biome.location())); } diff --git a/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeBuilder.java b/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeBuilder.java new file mode 100644 index 00000000000..785b867e02d --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeBuilder.java @@ -0,0 +1,170 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.biome; + +import net.minecraft.core.Holder; +import net.minecraft.sounds.Music; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.attribute.AmbientAdditionsSettings; +import net.minecraft.world.attribute.AmbientMoodSettings; +import net.minecraft.world.attribute.AmbientParticle; +import net.minecraft.world.attribute.AmbientSounds; +import net.minecraft.world.attribute.BackgroundMusic; +import net.minecraft.world.attribute.EnvironmentAttributeMap; +import net.minecraft.world.attribute.EnvironmentAttributes; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.biome.BiomeGenerationSettings; +import net.minecraft.world.level.biome.BiomeSpecialEffects; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; +import org.spongepowered.api.data.DataManipulator; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.effect.sound.SoundType; +import org.spongepowered.api.entity.EntityCategory; +import org.spongepowered.api.entity.EntityType; +import org.spongepowered.api.world.biome.Biome; +import org.spongepowered.api.world.biome.ambient.SoundConfig; +import org.spongepowered.api.world.biome.climate.GrassColorModifiers; +import org.spongepowered.api.world.biome.climate.TemperatureModifiers; +import org.spongepowered.api.world.biome.spawner.NaturalSpawnCost; +import org.spongepowered.api.world.biome.spawner.NaturalSpawner; +import org.spongepowered.api.world.generation.carver.Carver; +import org.spongepowered.api.world.generation.feature.DecorationStep; +import org.spongepowered.api.world.generation.feature.PlacedFeature; +import org.spongepowered.common.data.SpongeDataManager; +import org.spongepowered.common.data.provider.DataProviderLookup; +import org.spongepowered.common.data.provider.world.biome.BiomeData; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public final class SpongeBiomeBuilder implements Biome.Builder { + + private static final DataProviderLookup PROVIDER_LOOKUP = SpongeDataManager.getProviderRegistry().getProviderLookup(net.minecraft.world.level.biome.Biome.class); + + private DataManipulator.Mutable manipulator = DataManipulator.mutableOf(); + + public SpongeBiomeBuilder() { + this.reset(); + } + + @Override + public Biome.Builder add(final Key> key, final V value) { + if (!PROVIDER_LOOKUP.getProvider(key).isSupported(net.minecraft.world.level.biome.Biome.class)) { + throw new IllegalArgumentException(key + " is not supported for biomes"); + } + this.manipulator.set(key, value); + return this; + } + + @Override + public Biome.Builder reset() { + this.manipulator = DataManipulator.mutableOf(); + return this; + } + + @Override + public Biome.Builder from(final org.spongepowered.api.world.biome.Biome biome) { + this.manipulator.set(biome.getValues()); + return this; + } + + @Override + public Biome build() { + final var precipitation = this.manipulator.require(Keys.HAS_PRECIPITATION); + final Double temperature = this.manipulator.require(Keys.BIOME_TEMPERATURE); + final Double downfall = this.manipulator.require(Keys.HUMIDITY); + final var temperatureModifier = this.manipulator.getOrElse(Keys.TEMPERATURE_MODIFIER, TemperatureModifiers.NONE.get()); + + final var fogColor = this.manipulator.require(Keys.FOG_COLOR); + final var waterColor = this.manipulator.require(Keys.WATER_COLOR); + final var waterFogColor = this.manipulator.require(Keys.WATER_FOG_COLOR); + final var skyColor = this.manipulator.require(Keys.SKY_COLOR); + final var foliageColor = this.manipulator.get(Keys.FOLIAGE_COLOR); + final var grassColor = this.manipulator.get(Keys.GRASS_COLOR); + final var grassColorModifier = this.manipulator.getOrElse(Keys.GRASS_COLOR_MODIFIER, GrassColorModifiers.NONE.get()); + final var particleSettings = this.manipulator.get(Keys.AMBIENT_PARTICLE); + final Optional ambientSound = this.manipulator.get(Keys.AMBIENT_SOUND); + final Optional ambientMood = this.manipulator.get(Keys.AMBIENT_MOOD); + final Optional additionalSound = this.manipulator.get(Keys.AMBIENT_ADDITIONAL_SOUND); + final Optional backgroundMusic = this.manipulator.get(Keys.BACKGROUND_MUSIC); + + final Double spawnChance = this.manipulator.require(Keys.SPAWN_CHANCE); + final Map> spawners = this.manipulator.getOrElse(Keys.NATURAL_SPAWNERS, Map.of()); + final Map, NaturalSpawnCost> spawnerCosts = this.manipulator.getOrElse(Keys.NATURAL_SPAWNER_COST, Map.of()); + + final Map> features = this.manipulator.getOrElse(Keys.FEATURES, Map.of()); + final List carvers = this.manipulator.getOrElse(Keys.CARVERS, List.of()); + + final var attributes = EnvironmentAttributeMap.builder(); + attributes.set(EnvironmentAttributes.FOG_COLOR, fogColor.rgb()) + .set(EnvironmentAttributes.WATER_FOG_COLOR, waterFogColor.rgb()) + .set(EnvironmentAttributes.SKY_COLOR, skyColor.rgb()); + final BiomeSpecialEffects.Builder effectsBuilder = new BiomeSpecialEffects.Builder() + .waterColor(waterColor.rgb()) + .grassColorModifier((BiomeSpecialEffects.GrassColorModifier) (Object) grassColorModifier); + foliageColor.ifPresent(c -> effectsBuilder.foliageColorOverride(c.rgb())); + grassColor.ifPresent(c -> effectsBuilder.grassColorOverride(c.rgb())); + final var vanillaAmbientSound = new AmbientSounds( + ambientSound.map(SoundEvent.class::cast).map(Holder::direct), + ambientMood.map(AmbientMoodSettings.class::cast), + additionalSound.map(AmbientAdditionsSettings.class::cast).map(List::of).orElse(List.of())); + attributes.set(EnvironmentAttributes.AMBIENT_SOUNDS, vanillaAmbientSound); + + backgroundMusic.ifPresent(m -> attributes.set(EnvironmentAttributes.BACKGROUND_MUSIC, new BackgroundMusic((Music) (Object) m))); + + final MobSpawnSettings.Builder spawnerBuilder = new MobSpawnSettings.Builder() + .creatureGenerationProbability(spawnChance.floatValue()); + spawners.forEach((cat, spawner) -> spawner.forEach(sp -> { + final var data = ((BiomeData.WeightedSpanwer) sp).data(); + spawnerBuilder.addSpawn((MobCategory) (Object) cat, data.weight(), data.value()); + })); + spawnerCosts.forEach((type, cost) -> spawnerBuilder.addMobCharge((net.minecraft.world.entity.EntityType) (Object) type, cost.budget(), + cost.charge())); + + final BiomeGenerationSettings.PlainBuilder generationBuilder = new BiomeGenerationSettings.PlainBuilder(); + features.forEach((step, list) -> list.forEach(feature -> generationBuilder.addFeature((GenerationStep.Decoration) (Object) step, + Holder.direct((net.minecraft.world.level.levelgen.placement.PlacedFeature) (Object) feature)))); + carvers.forEach((carver) -> generationBuilder.addCarver(Holder.direct((ConfiguredWorldCarver) (Object) carver))); + + final net.minecraft.world.level.biome.Biome.BiomeBuilder vanillaBuilder = new net.minecraft.world.level.biome.Biome.BiomeBuilder() + .hasPrecipitation(precipitation) + .temperature(temperature.floatValue()) + .downfall(downfall.floatValue()) + .temperatureAdjustment((net.minecraft.world.level.biome.Biome.TemperatureModifier) (Object) temperatureModifier) + .specialEffects(effectsBuilder.build()) + .mobSpawnSettings(spawnerBuilder.build()) + .generationSettings(generationBuilder.build()) + .putAttributes(attributes.build()) + ; + particleSettings.ifPresent(ps -> vanillaBuilder.setAttribute(EnvironmentAttributes.AMBIENT_PARTICLES, List.of((AmbientParticle) (Object) ps))); + + return (Biome) (Object) vanillaBuilder.build(); + } +} diff --git a/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeProviderFactory.java b/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeProviderFactory.java index 44d1b9fdcd8..515a576fc3e 100644 --- a/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeProviderFactory.java +++ b/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeProviderFactory.java @@ -29,8 +29,8 @@ import net.minecraft.core.HolderSet; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.CheckerboardColumnBiomeSource; import net.minecraft.world.level.biome.Climate.ParameterList; @@ -61,7 +61,7 @@ public final class SpongeBiomeProviderFactory implements BiomeProvider.Factory { @Override public ConfigurableBiomeProvider overworld() { final var registry = SpongeCommon.vanillaRegistry(Registries.MULTI_NOISE_BIOME_SOURCE_PARAMETER_LIST); - final var holder = registry.getHolderOrThrow(MultiNoiseBiomeSourceParameterLists.OVERWORLD); + final var holder = registry.getOrThrow(MultiNoiseBiomeSourceParameterLists.OVERWORLD); return (ConfigurableBiomeProvider) MultiNoiseBiomeSource.createFromPreset(holder); } @@ -77,7 +77,7 @@ public ConfigurableBiomeProvider multiNoise(final MultiNo @Override public ConfigurableBiomeProvider nether() { final var registry = SpongeCommon.vanillaRegistry(Registries.MULTI_NOISE_BIOME_SOURCE_PARAMETER_LIST); - final var holder = registry.getHolderOrThrow(MultiNoiseBiomeSourceParameterLists.NETHER); + final var holder = registry.getOrThrow(MultiNoiseBiomeSourceParameterLists.NETHER); return (ConfigurableBiomeProvider) MultiNoiseBiomeSource.createFromPreset(holder); } @@ -94,7 +94,7 @@ public ConfigurableBiomeProvider endStyle(final EndStyleBio @Override public ConfigurableBiomeProvider end() { - return (ConfigurableBiomeProvider) TheEndBiomeSource.create(this.registry().asLookup()); + return (ConfigurableBiomeProvider) TheEndBiomeSource.create(this.registry()); } @Override @@ -114,6 +114,6 @@ private Registry registry() { } private Holder biomeHolder(final RegistryReference biome) { - return this.registry().getHolderOrThrow(ResourceKey.create(Registries.BIOME, (ResourceLocation) (Object) biome.location())); + return this.registry().getOrThrow(ResourceKey.create(Registries.BIOME, (Identifier) (Object) biome.location())); } } diff --git a/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeTemplate.java b/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeTemplate.java deleted file mode 100644 index cea046cce1d..00000000000 --- a/src/main/java/org/spongepowered/common/world/biome/SpongeBiomeTemplate.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.biome; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.RegistryOps; -import net.minecraft.sounds.Music; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.level.biome.AmbientAdditionsSettings; -import net.minecraft.world.level.biome.AmbientParticleSettings; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeGenerationSettings; -import net.minecraft.world.level.biome.BiomeSpecialEffects; -import net.minecraft.world.level.biome.MobSpawnSettings; -import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.DataManipulator; -import org.spongepowered.api.data.Key; -import org.spongepowered.api.data.Keys; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.data.value.Value; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.effect.sound.SoundType; -import org.spongepowered.api.entity.EntityCategory; -import org.spongepowered.api.entity.EntityType; -import org.spongepowered.api.world.biome.BiomeTemplate; -import org.spongepowered.api.world.biome.ambient.SoundConfig; -import org.spongepowered.api.world.biome.climate.GrassColorModifiers; -import org.spongepowered.api.world.biome.climate.TemperatureModifiers; -import org.spongepowered.api.world.biome.spawner.NaturalSpawnCost; -import org.spongepowered.api.world.biome.spawner.NaturalSpawner; -import org.spongepowered.api.world.generation.carver.Carver; -import org.spongepowered.api.world.generation.carver.CarvingStep; -import org.spongepowered.api.world.generation.feature.DecorationStep; -import org.spongepowered.api.world.generation.feature.PlacedFeature; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.data.SpongeDataManager; -import org.spongepowered.common.data.provider.DataProviderLookup; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - -public record SpongeBiomeTemplate(ResourceKey key, Biome representedBiome, DataPack pack) implements BiomeTemplate { - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeBiomeTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Biome:\n" + serialized, e); - } - } - - @Override - public org.spongepowered.api.world.biome.Biome biome() { - return (org.spongepowered.api.world.biome.Biome) (Object) this.representedBiome; - } - - public static JsonElement encode(final BiomeTemplate template, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return Biome.DIRECT_CODEC.encodeStart(ops, (Biome) (Object) template.biome()).getOrThrow(); - } - - public static Biome decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return Biome.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static BiomeTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final Biome parsed = SpongeBiomeTemplate.decode(packEntry, registryAccess); - return new SpongeBiomeTemplate(key, parsed, pack); - } - - public static class BuilderImpl extends AbstractDataPackEntryBuilder implements BiomeTemplate.Builder { - - private static DataProviderLookup PROVIDER_LOOKUP = SpongeDataManager.getProviderRegistry().getProviderLookup(Biome.class); - - private DataManipulator.Mutable manipulator = DataManipulator.mutableOf(); - - public BuilderImpl() { - this.reset(); - } - - @Override - public Function valueExtractor() { - return BiomeTemplate::biome; - } - - @Override - public Builder add(final Key> key, final V value) { - if (!PROVIDER_LOOKUP.getProvider(key).isSupported(Biome.class)) { - throw new IllegalArgumentException(key + " is not supported for biomes"); - } - this.manipulator.set(key, value); - return this; - } - - @Override - public Builder reset() { - this.manipulator = DataManipulator.mutableOf(); - this.key = null; - this.pack = DataPacks.BIOME; - return this; - } - - @Override - public Builder fromValue(final org.spongepowered.api.world.biome.Biome biome) { - this.manipulator.set(biome.getValues()); - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - final Biome biome = SpongeBiomeTemplate.decode(json, SpongeCommon.server().registryAccess()); - this.fromValue((org.spongepowered.api.world.biome.Biome) (Object) biome); - return this; - } - - @Override - protected BiomeTemplate build0() { - final var precipitation = this.manipulator.require(Keys.HAS_PRECIPITATION); - final Double temperature = this.manipulator.require(Keys.BIOME_TEMPERATURE); - final Double downfall = this.manipulator.require(Keys.HUMIDITY); - final var temperatureModifier = this.manipulator.getOrElse(Keys.TEMPERATURE_MODIFIER, TemperatureModifiers.NONE.get()); - - final var fogColor = this.manipulator.require(Keys.FOG_COLOR); - final var waterColor = this.manipulator.require(Keys.WATER_COLOR); - final var waterFogColor = this.manipulator.require(Keys.WATER_FOG_COLOR); - final var skyColor = this.manipulator.require(Keys.SKY_COLOR); - final var foliageColor = this.manipulator.get(Keys.FOLIAGE_COLOR); - final var grassColor = this.manipulator.get(Keys.GRASS_COLOR); - final var grassColorModifier = this.manipulator.getOrElse(Keys.GRASS_COLOR_MODIFIER, GrassColorModifiers.NONE.get()); - final var particleSettings = this.manipulator.get(Keys.AMBIENT_PARTICLE); - final Optional ambientSound = this.manipulator.get(Keys.AMBIENT_SOUND); - final Optional ambientMood = this.manipulator.get(Keys.AMBIENT_MOOD); - final Optional additionalSound = this.manipulator.get(Keys.AMBIENT_ADDITIONAL_SOUND); - final Optional backgroundMusic = this.manipulator.get(Keys.BACKGROUND_MUSIC); - - final Double spawnChance = this.manipulator.require(Keys.SPAWN_CHANCE); - final Map> spawners = this.manipulator.getOrElse(Keys.NATURAL_SPAWNERS, Map.of()); - final Map, NaturalSpawnCost> spawnerCosts = this.manipulator.getOrElse(Keys.NATURAL_SPAWNER_COST, Map.of()); - - final Map> features = this.manipulator.getOrElse(Keys.FEATURES, Map.of()); - final Map> carvers = this.manipulator.getOrElse(Keys.CARVERS, Map.of()); - - final BiomeSpecialEffects.Builder effectsBuilder = new BiomeSpecialEffects.Builder() - .fogColor(fogColor.rgb()) - .waterColor(waterColor.rgb()) - .waterFogColor(waterFogColor.rgb()) - .skyColor(skyColor.rgb()) - .grassColorModifier((BiomeSpecialEffects.GrassColorModifier) (Object) grassColorModifier); - foliageColor.ifPresent(c -> effectsBuilder.foliageColorOverride(c.rgb())); - grassColor.ifPresent(c -> effectsBuilder.grassColorOverride(c.rgb())); - particleSettings.ifPresent(ps -> effectsBuilder.ambientParticle((AmbientParticleSettings) ps)); - ambientSound.ifPresent(s -> effectsBuilder.ambientLoopSound(Holder.direct((SoundEvent) s))); - ambientMood.ifPresent(m -> effectsBuilder.ambientMoodSound((net.minecraft.world.level.biome.AmbientMoodSettings) m)); - additionalSound.ifPresent(s -> effectsBuilder.ambientAdditionsSound((AmbientAdditionsSettings) s)); - backgroundMusic.ifPresent(m -> effectsBuilder.backgroundMusic((Music) m)); - - final MobSpawnSettings.Builder spawnerBuilder = new MobSpawnSettings.Builder() - .creatureGenerationProbability(spawnChance.floatValue()); - spawners.forEach((cat, spawner) -> spawner.forEach(sp -> { - spawnerBuilder.addSpawn((MobCategory) (Object) cat, (MobSpawnSettings.SpawnerData) sp); - })); - spawnerCosts.forEach((type, cost) -> spawnerBuilder.addMobCharge((net.minecraft.world.entity.EntityType) (Object) type, cost.budget(), - cost.charge())); - - final Registry placedFeatureRegistry = SpongeCommon.vanillaRegistry(Registries.PLACED_FEATURE); - final Registry> configuredWorldCarverRegistry = SpongeCommon.vanillaRegistry(Registries.CONFIGURED_CARVER); - final BiomeGenerationSettings.Builder generationBuilder = new BiomeGenerationSettings.Builder(placedFeatureRegistry.asLookup(), configuredWorldCarverRegistry.asLookup()); - features.forEach((step, list) -> list.forEach(feature -> generationBuilder.addFeature((GenerationStep.Decoration) (Object) step, - Holder.direct((net.minecraft.world.level.levelgen.placement.PlacedFeature) (Object) feature)))); - carvers.forEach((step, list) -> list.forEach(carver -> generationBuilder.addCarver((GenerationStep.Carving) (Object) step, - Holder.direct((ConfiguredWorldCarver) (Object) carver)))); - - final Biome.BiomeBuilder vanillaBuilder = new Biome.BiomeBuilder() - .hasPrecipitation(precipitation) - .temperature(temperature.floatValue()) - .downfall(downfall.floatValue()) - .temperatureAdjustment((Biome.TemperatureModifier) (Object) temperatureModifier) - .specialEffects(effectsBuilder.build()) - .mobSpawnSettings(spawnerBuilder.build()) - .generationSettings(generationBuilder.build()); - return new SpongeBiomeTemplate(this.key, vanillaBuilder.build(), this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/biome/ambient/SpongeParticleConfigFactory.java b/src/main/java/org/spongepowered/common/world/biome/ambient/SpongeParticleConfigFactory.java index 104cd5051ec..6bc1657f8dd 100644 --- a/src/main/java/org/spongepowered/common/world/biome/ambient/SpongeParticleConfigFactory.java +++ b/src/main/java/org/spongepowered/common/world/biome/ambient/SpongeParticleConfigFactory.java @@ -25,7 +25,7 @@ package org.spongepowered.common.world.biome.ambient; import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.world.level.biome.AmbientParticleSettings; +import net.minecraft.world.attribute.AmbientParticle; import org.spongepowered.api.effect.particle.ParticleType; import org.spongepowered.api.world.biome.ambient.ParticleConfig; @@ -33,6 +33,6 @@ public class SpongeParticleConfigFactory implements ParticleConfig.Factory { @Override public ParticleConfig of(final ParticleType type, final float probability) { - return (ParticleConfig) new AmbientParticleSettings(((ParticleOptions) type), probability); + return (ParticleConfig) (Object) new AmbientParticle(((ParticleOptions) type), probability); } } diff --git a/src/main/java/org/spongepowered/common/world/biome/ambient/SpongeSoundConfigFactory.java b/src/main/java/org/spongepowered/common/world/biome/ambient/SpongeSoundConfigFactory.java index 425d535c24c..7fcb108a956 100644 --- a/src/main/java/org/spongepowered/common/world/biome/ambient/SpongeSoundConfigFactory.java +++ b/src/main/java/org/spongepowered/common/world/biome/ambient/SpongeSoundConfigFactory.java @@ -27,8 +27,8 @@ import net.minecraft.core.Holder; import net.minecraft.sounds.Music; import net.minecraft.sounds.SoundEvent; -import net.minecraft.world.level.biome.AmbientAdditionsSettings; -import net.minecraft.world.level.biome.AmbientMoodSettings; +import net.minecraft.world.attribute.AmbientAdditionsSettings; +import net.minecraft.world.attribute.AmbientMoodSettings; import org.spongepowered.api.effect.sound.SoundType; import org.spongepowered.api.world.biome.ambient.SoundConfig; @@ -36,16 +36,16 @@ public class SpongeSoundConfigFactory implements SoundConfig.Factory { @Override public SoundConfig.Mood ofAmbientMood(final SoundType sound, final int tickDelay, final int searchRadius, final double distanceModifier) { - return (SoundConfig.Mood) new AmbientMoodSettings(Holder.direct((SoundEvent) sound), tickDelay, searchRadius, distanceModifier); + return (SoundConfig.Mood) (Object) new AmbientMoodSettings(Holder.direct((SoundEvent) (Object) sound), tickDelay, searchRadius, distanceModifier); } @Override public SoundConfig.Additional ofAdditional(final SoundType sound, final double tickChance) { - return (SoundConfig.Additional) new AmbientAdditionsSettings(Holder.direct((SoundEvent) sound), tickChance); + return (SoundConfig.Additional) (Object) new AmbientAdditionsSettings(Holder.direct((SoundEvent) (Object) sound), tickChance); } @Override public SoundConfig.BackgroundMusic ofBackroundMusic(final SoundType sound, final int minDelay, final int maxDelay, final boolean replacesCurrent) { - return (SoundConfig.BackgroundMusic) new Music(Holder.direct((SoundEvent) sound), minDelay, maxDelay, replacesCurrent); + return (SoundConfig.BackgroundMusic) (Object) new Music(Holder.direct((SoundEvent) (Object) sound), minDelay, maxDelay, replacesCurrent); } } diff --git a/src/main/java/org/spongepowered/common/world/biome/provider/SpongeMultiNoiseBiomeConfig.java b/src/main/java/org/spongepowered/common/world/biome/provider/SpongeMultiNoiseBiomeConfig.java index 8ce6d49e842..8d6a39836ad 100644 --- a/src/main/java/org/spongepowered/common/world/biome/provider/SpongeMultiNoiseBiomeConfig.java +++ b/src/main/java/org/spongepowered/common/world/biome/provider/SpongeMultiNoiseBiomeConfig.java @@ -131,7 +131,7 @@ public static final class FactoryImpl implements Factory { @Override public MultiNoiseBiomeConfig nether() { final var registry = SpongeCommon.vanillaRegistry(Registries.MULTI_NOISE_BIOME_SOURCE_PARAMETER_LIST); - final var holder = registry.getHolderOrThrow(MultiNoiseBiomeSourceParameterLists.NETHER); + final var holder = registry.getOrThrow(MultiNoiseBiomeSourceParameterLists.NETHER); final var biomeSource = (MultiNoiseBiomeSourceAccessor) MultiNoiseBiomeSource.createFromPreset(holder); return new BuilderImpl().addMcBiomes(biomeSource.accessor$parameters()).build(); } @@ -139,7 +139,7 @@ public MultiNoiseBiomeConfig nether() { @Override public MultiNoiseBiomeConfig overworld() { final var registry = SpongeCommon.vanillaRegistry(Registries.MULTI_NOISE_BIOME_SOURCE_PARAMETER_LIST); - final var holder = registry.getHolderOrThrow(MultiNoiseBiomeSourceParameterLists.OVERWORLD); + final var holder = registry.getOrThrow(MultiNoiseBiomeSourceParameterLists.OVERWORLD); final var biomeSource = (MultiNoiseBiomeSourceAccessor) MultiNoiseBiomeSource.createFromPreset(holder); return new BuilderImpl().addMcBiomes(biomeSource.accessor$parameters()).build(); } diff --git a/src/main/java/org/spongepowered/common/world/biome/spawner/SpongeNaturalSpawnerFactory.java b/src/main/java/org/spongepowered/common/world/biome/spawner/SpongeNaturalSpawnerFactory.java index 03b5f845d7b..577c7827ea5 100644 --- a/src/main/java/org/spongepowered/common/world/biome/spawner/SpongeNaturalSpawnerFactory.java +++ b/src/main/java/org/spongepowered/common/world/biome/spawner/SpongeNaturalSpawnerFactory.java @@ -24,14 +24,16 @@ */ package org.spongepowered.common.world.biome.spawner; +import net.minecraft.util.random.Weighted; import net.minecraft.world.level.biome.MobSpawnSettings; import org.spongepowered.api.entity.EntityType; import org.spongepowered.api.world.biome.spawner.NaturalSpawner; +import org.spongepowered.common.data.provider.world.biome.BiomeData; public class SpongeNaturalSpawnerFactory implements NaturalSpawner.Factory { @Override public NaturalSpawner of(final EntityType type, final int weight, final int min, final int max) { - return (NaturalSpawner) new MobSpawnSettings.SpawnerData((net.minecraft.world.entity.EntityType) type, weight, min, max); + return new BiomeData.WeightedSpanwer(new Weighted<>(new MobSpawnSettings.SpawnerData((net.minecraft.world.entity.EntityType) type, min, max), weight)); } } diff --git a/src/main/java/org/spongepowered/common/world/border/PlayerOwnBorderListener.java b/src/main/java/org/spongepowered/common/world/border/PlayerOwnBorderListener.java index 52d95011a16..53ab10aa799 100644 --- a/src/main/java/org/spongepowered/common/world/border/PlayerOwnBorderListener.java +++ b/src/main/java/org/spongepowered/common/world/border/PlayerOwnBorderListener.java @@ -46,38 +46,39 @@ public PlayerOwnBorderListener(final net.minecraft.server.level.ServerPlayer pla } @Override - public void onBorderSizeSet(final WorldBorder border, final double newSize) { + public void onSetSize(final WorldBorder border, final double newSize) { this.sendBorderPacket(new ClientboundSetBorderSizePacket(border)); } @Override - public void onBorderSizeLerping(final WorldBorder border, final double oldSize, final double newSize, final long time) { + public void onLerpSize(WorldBorder border, double oldSize, double newSize, long time, long var8) { this.sendBorderPacket(new ClientboundSetBorderLerpSizePacket(border)); } @Override - public void onBorderCenterSet(final WorldBorder border, final double x, final double z) { + public void onSetCenter(final WorldBorder border, final double x, final double z) { this.sendBorderPacket(new ClientboundSetBorderCenterPacket(border)); } @Override - public void onBorderSetWarningTime(final WorldBorder border, final int newTime) { + public void onSetWarningTime(final WorldBorder border, final int newTime) { this.sendBorderPacket(new ClientboundSetBorderWarningDelayPacket(border)); } @Override - public void onBorderSetWarningBlocks(final WorldBorder border, final int newDistance) { + public void onSetWarningBlocks(final WorldBorder border, final int newDistance) { this.sendBorderPacket(new ClientboundSetBorderWarningDistancePacket(border)); } @Override - public void onBorderSetDamagePerBlock(final WorldBorder border, final double newAmount) { + public void onSetDamagePerBlock(final WorldBorder border, final double newAmount) { } @Override - public void onBorderSetDamageSafeZOne(final WorldBorder border, final double newSize) { + public void onSetSafeZone(final WorldBorder border, final double newSize) { } + /** * This method is for cleaning up the player reference once they disconnect. */ @@ -91,4 +92,5 @@ public void onPlayerDisconnect() { private void sendBorderPacket(final Packet packet) { this.player.connection.send(packet); } + } diff --git a/src/main/java/org/spongepowered/common/world/border/SpongeWorldBorderBuilder.java b/src/main/java/org/spongepowered/common/world/border/SpongeWorldBorderBuilder.java index 550cb0c61f6..64505ca9ed6 100644 --- a/src/main/java/org/spongepowered/common/world/border/SpongeWorldBorderBuilder.java +++ b/src/main/java/org/spongepowered/common/world/border/SpongeWorldBorderBuilder.java @@ -24,20 +24,20 @@ */ package org.spongepowered.common.world.border; +import org.spongepowered.api.util.Ticks; import org.spongepowered.api.world.border.WorldBorder; import org.spongepowered.common.accessor.world.level.border.WorldBorder_SettingsAccessor; import org.spongepowered.common.bridge.world.level.border.WorldBorderBridge; +import org.spongepowered.common.util.SpongeTicks; import org.spongepowered.math.vector.Vector2d; -import java.time.Duration; - public final class SpongeWorldBorderBuilder implements WorldBorder.Builder { private double diameter = -1; private double initialDiameter = -1; - private Duration time = Duration.ZERO; + private Ticks time = Ticks.zero(); private Vector2d center = Vector2d.ZERO; //use a default value otherwise null is used - private Duration warningTime = Duration.ZERO; + private Ticks warningTime = Ticks.zero(); private double warningDistance; private double safeZone; private double damagePerBlock; @@ -61,7 +61,7 @@ public WorldBorder.Builder from(final WorldBorder border) { @Override public WorldBorder.Builder overworldDefaults() { - return this.from((WorldBorder) net.minecraft.world.level.border.WorldBorder.DEFAULT_SETTINGS); + return this.from((WorldBorder) (Object) net.minecraft.world.level.border.WorldBorder.Settings.DEFAULT); } @Override @@ -77,11 +77,8 @@ public WorldBorder.Builder targetDiameter(final double diameter) { } @Override - public WorldBorder.Builder timeToTargetDiameter(final Duration time) { - if (time.isNegative()) { - throw new IllegalArgumentException("time cannot be negative"); - } - this.time = time; + public WorldBorder.Builder timeToTargetDiameter(final Ticks ticks) { + this.time = ticks; return this; } @@ -122,10 +119,7 @@ public WorldBorder.Builder damagePerBlock(final double damagePerBlock) { } @Override - public WorldBorder.Builder warningTime(final Duration warningTime) { - if (warningTime.isNegative()) { - throw new IllegalArgumentException("warning time cannot be negative"); - } + public WorldBorder.Builder warningTime(final Ticks warningTime) { this.warningTime = warningTime; return this; } @@ -145,15 +139,15 @@ public WorldBorder build() throws IllegalStateException { if (this.diameter == -1) { throw new IllegalStateException("The diameter or initial diameter has not been set!"); } - return (WorldBorder) WorldBorder_SettingsAccessor.invoker$new( + return (WorldBorder) (Object) WorldBorder_SettingsAccessor.invoker$new( this.center.x(), this.center.y(), this.damagePerBlock, this.safeZone, (int) this.warningDistance, - (int) this.warningTime.getSeconds(), + SpongeTicks.toSaturatedIntOrInfinite(this.warningTime), this.initialDiameter == -1 ? this.diameter : this.initialDiameter, - this.time.toMillis(), + SpongeTicks.toSaturatedLongOrInfinite(this.time), this.diameter ); } @@ -165,9 +159,9 @@ public WorldBorder.Builder reset() { this.safeZone = 0; this.diameter = -1; this.initialDiameter = -1; - this.time = Duration.ZERO; + this.time = Ticks.zero(); this.warningDistance = 0; - this.warningTime = Duration.ZERO; + this.warningTime = Ticks.zero(); return this; } diff --git a/src/main/java/org/spongepowered/common/world/generation/SpongeChunkGeneratorFactory.java b/src/main/java/org/spongepowered/common/world/generation/SpongeChunkGeneratorFactory.java index 81c290117ee..59634ec1fa3 100644 --- a/src/main/java/org/spongepowered/common/world/generation/SpongeChunkGeneratorFactory.java +++ b/src/main/java/org/spongepowered/common/world/generation/SpongeChunkGeneratorFactory.java @@ -24,8 +24,6 @@ */ package org.spongepowered.common.world.generation; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; import net.minecraft.world.level.biome.BiomeSource; @@ -36,17 +34,13 @@ import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; import org.spongepowered.api.world.biome.provider.BiomeProvider; import org.spongepowered.api.world.generation.ChunkGenerator; import org.spongepowered.api.world.generation.ConfigurableChunkGenerator; import org.spongepowered.api.world.generation.config.flat.FlatGeneratorConfig; import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfig; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.world.server.SpongeWorldTemplate; -import java.io.IOException; import java.util.Objects; @SuppressWarnings("unchecked") @@ -72,31 +66,25 @@ public ConfigurableChunkGenerator noise(final BiomeProvide public ConfigurableChunkGenerator overworld() { var noiseGeneratorSettingsRegistry = SpongeCommon.vanillaRegistry(Registries.NOISE_SETTINGS); final var registry = SpongeCommon.vanillaRegistry(Registries.MULTI_NOISE_BIOME_SOURCE_PARAMETER_LIST); - final var holder = registry.getHolderOrThrow(MultiNoiseBiomeSourceParameterLists.OVERWORLD); + final var holder = registry.getOrThrow(MultiNoiseBiomeSourceParameterLists.OVERWORLD); final var biomeSource = MultiNoiseBiomeSource.createFromPreset(holder); - return this.noiseBasedChunkGenerator(biomeSource, noiseGeneratorSettingsRegistry.getHolderOrThrow(NoiseGeneratorSettings.OVERWORLD)); + return this.noiseBasedChunkGenerator(biomeSource, noiseGeneratorSettingsRegistry.getOrThrow(NoiseGeneratorSettings.OVERWORLD)); } @Override public ConfigurableChunkGenerator theNether() { var noiseGeneratorSettingsRegistry = SpongeCommon.vanillaRegistry(Registries.NOISE_SETTINGS); final var registry = SpongeCommon.vanillaRegistry(Registries.MULTI_NOISE_BIOME_SOURCE_PARAMETER_LIST); - final var holder = registry.getHolderOrThrow(MultiNoiseBiomeSourceParameterLists.NETHER); + final var holder = registry.getOrThrow(MultiNoiseBiomeSourceParameterLists.NETHER); final var biomeSource = MultiNoiseBiomeSource.createFromPreset(holder); - return this.noiseBasedChunkGenerator(biomeSource, noiseGeneratorSettingsRegistry.getHolderOrThrow(NoiseGeneratorSettings.NETHER)); + return this.noiseBasedChunkGenerator(biomeSource, noiseGeneratorSettingsRegistry.getOrThrow(NoiseGeneratorSettings.NETHER)); } @Override public ConfigurableChunkGenerator theEnd() { var biomeRegistry = SpongeCommon.vanillaRegistry(Registries.BIOME); var noiseGeneratorSettingsRegistry = SpongeCommon.vanillaRegistry(Registries.NOISE_SETTINGS); - var biomeSource = TheEndBiomeSource.create(biomeRegistry.asLookup()); - return this.noiseBasedChunkGenerator(biomeSource, noiseGeneratorSettingsRegistry.getHolderOrThrow(NoiseGeneratorSettings.END)); - } - - @Override - public ChunkGenerator fromDataPack(DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - return (ChunkGenerator) SpongeWorldTemplate.decodeStem(json, SpongeCommon.server().registryAccess()).generator(); + var biomeSource = TheEndBiomeSource.create(biomeRegistry); + return this.noiseBasedChunkGenerator(biomeSource, noiseGeneratorSettingsRegistry.getOrThrow(NoiseGeneratorSettings.END)); } } diff --git a/src/main/java/org/spongepowered/common/world/generation/SpongeWorldGenerationConfigBuilder.java b/src/main/java/org/spongepowered/common/world/generation/SpongeWorldGenerationConfigBuilder.java index 4a2c4806dce..9fe37611274 100644 --- a/src/main/java/org/spongepowered/common/world/generation/SpongeWorldGenerationConfigBuilder.java +++ b/src/main/java/org/spongepowered/common/world/generation/SpongeWorldGenerationConfigBuilder.java @@ -61,7 +61,7 @@ public WorldGenerationConfig.Builder generateBonusChest(final boolean generateBo @Override public WorldGenerationConfig.Builder reset() { - final WorldOptions defaultSettings = SpongeCommon.server().getWorldData().worldGenOptions(); + final WorldOptions defaultSettings = SpongeCommon.server().getWorldGenSettings().options(); this.seed = defaultSettings.seed(); this.generateFeatures = defaultSettings.generateStructures(); this.generateBonusChest = defaultSettings.generateBonusChest(); diff --git a/src/main/java/org/spongepowered/common/world/generation/carver/SpongeCarverBuilder.java b/src/main/java/org/spongepowered/common/world/generation/carver/SpongeCarverBuilder.java new file mode 100644 index 00000000000..8bdc6956d0f --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/generation/carver/SpongeCarverBuilder.java @@ -0,0 +1,67 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.generation.carver; + +import net.minecraft.world.level.levelgen.carver.CarverConfiguration; +import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; +import net.minecraft.world.level.levelgen.carver.WorldCarver; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.world.generation.carver.Carver; +import org.spongepowered.api.world.generation.carver.CarverType; + +public final class SpongeCarverBuilder implements Carver.Builder { + + @Nullable private WorldCarver type; + @Nullable private CarverConfiguration config; + + public SpongeCarverBuilder() { + this.reset(); + } + + @Override + public Carver.Builder reset() { + this.type = null; + this.config = null; + return this; + } + + @Override + public Carver.Builder type(final CarverType type) { + this.type = (WorldCarver) type; + return this; + } + + @Override + public Carver.Builder from(final Carver carver) { + this.type(carver.type()); + this.config = ((ConfiguredWorldCarver) (Object) carver).config(); + return this; + } + + @Override + public Carver build() { + return (Carver) (Object) new ConfiguredWorldCarver<>((WorldCarver) this.type, this.config); + } +} diff --git a/src/main/java/org/spongepowered/common/world/generation/carver/SpongeCarverTemplate.java b/src/main/java/org/spongepowered/common/world/generation/carver/SpongeCarverTemplate.java deleted file mode 100644 index cefbb919521..00000000000 --- a/src/main/java/org/spongepowered/common/world/generation/carver/SpongeCarverTemplate.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.generation.carver; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.Codec; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.RegistryOps; -import net.minecraft.world.level.levelgen.carver.CarverConfiguration; -import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; -import net.minecraft.world.level.levelgen.carver.WorldCarver; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.world.generation.carver.Carver; -import org.spongepowered.api.world.generation.carver.CarverTemplate; -import org.spongepowered.api.world.generation.carver.CarverType; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.function.Function; - -public record SpongeCarverTemplate(ResourceKey key, ConfiguredWorldCarver representedCarver, DataPack pack) implements CarverTemplate { - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeCarverTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Carver:\n" + serialized, e); - } - } - - @Override - public Carver carver() { - return (Carver) (Object) this.representedCarver; - } - - public static JsonElement encode(final Codec> codec, final ConfiguredWorldCarver carver, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return codec.encodeStart(ops, carver).getOrThrow(); - } - - public static JsonElement encode(final CarverTemplate template, final RegistryAccess registryAccess) { - return SpongeCarverTemplate.encode(ConfiguredWorldCarver.DIRECT_CODEC, (ConfiguredWorldCarver) (Object) template.carver(), registryAccess); - } - - public static ConfiguredWorldCarver decode(final Codec> codec, final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return codec.parse(ops, json).getOrThrow(); - } - - public static CarverTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final ConfiguredWorldCarver parsed = SpongeCarverTemplate.decode(ConfiguredWorldCarver.DIRECT_CODEC, packEntry, registryAccess); - return new SpongeCarverTemplate(key, parsed, pack); - } - - public static class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - @Nullable private WorldCarver type; - @Nullable private CarverConfiguration config; - - @Override - public Function valueExtractor() { - return CarverTemplate::carver; - } - - @Override - public Builder reset() { - this.type = null; - this.config = null; - this.key = null; - this.pack = DataPacks.CARVER; - return this; - } - - @Override - public Builder type(final CarverType type) { - this.type = (WorldCarver) type; - return this; - } - - @Override - public Builder fromValue(final Carver carver) { - this.type(carver.type()); - this.config = ((ConfiguredWorldCarver) (Object) carver).config(); - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - final ConfiguredWorldCarver carver = SpongeCarverTemplate.decode(ConfiguredWorldCarver.DIRECT_CODEC, json, SpongeCommon.server().registryAccess()); - this.fromValue((Carver) (Object) carver); - return this; - } - - @Override - protected CarverTemplate build0() { - ConfiguredWorldCarver carver = new ConfiguredWorldCarver<>((WorldCarver) this.type, this.config); - return new SpongeCarverTemplate(this.key, carver, this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/generation/config/flat/SpongeFlatGeneratorConfig.java b/src/main/java/org/spongepowered/common/world/generation/config/flat/SpongeFlatGeneratorConfig.java index b458a4585ce..9a7c5823751 100644 --- a/src/main/java/org/spongepowered/common/world/generation/config/flat/SpongeFlatGeneratorConfig.java +++ b/src/main/java/org/spongepowered/common/world/generation/config/flat/SpongeFlatGeneratorConfig.java @@ -30,8 +30,8 @@ import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; import net.minecraft.data.worldgen.placement.MiscOverworldPlacements; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.placement.PlacedFeature; @@ -139,10 +139,10 @@ public FlatGeneratorConfig.Builder from(final FlatGeneratorConfig value) { throw new IllegalStateException("Flat generation requires at least 1 Layer!"); } final Registry biomeRegistry = SpongeCommon.vanillaRegistry(Registries.BIOME); - final HolderLookup.RegistryLookup placedFeatureRegistryLookup = SpongeCommon.vanillaRegistry(Registries.PLACED_FEATURE).asLookup(); + final HolderLookup.RegistryLookup placedFeatureRegistryLookup = SpongeCommon.vanillaRegistry(Registries.PLACED_FEATURE); final Holder.Reference biome = - biomeRegistry.asLookup().getOrThrow(ResourceKey.create(Registries.BIOME, (ResourceLocation) (Object) this.biome.location())); + biomeRegistry.getOrThrow(ResourceKey.create(Registries.BIOME, (Identifier) (Object) this.biome.location())); return (FlatGeneratorConfig) FlatLevelGeneratorSettingsAccessor.invoker$new( this.structureSets == null ? Optional.empty() : Optional.of(HolderSet.direct((Function) Holder::direct, this.structureSets)), (List) (Object) this.layers, diff --git a/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeDensityFunctionTemplate.java b/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeDensityFunctionTemplate.java deleted file mode 100644 index 7d52ebff2c2..00000000000 --- a/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeDensityFunctionTemplate.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.generation.config.noise; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.RegistryOps; -import net.minecraft.world.level.levelgen.DensityFunction; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.world.generation.config.noise.DensityFunctionTemplate; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.Objects; -import java.util.function.Function; - -public record SpongeDensityFunctionTemplate(ResourceKey key, DensityFunction representedFunction, DataPack pack) implements DensityFunctionTemplate { - - @Override - public org.spongepowered.api.world.generation.config.noise.DensityFunction densityFunction() { - return (org.spongepowered.api.world.generation.config.noise.DensityFunction) this.representedFunction; - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeDensityFunctionTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Density Function:\n" + serialized, e); - } - } - - public static JsonElement encode(final DensityFunctionTemplate template, final RegistryAccess registryAccess) { - return DensityFunction.DIRECT_CODEC.encodeStart(JsonOps.INSTANCE, (DensityFunction) template.densityFunction()).getOrThrow(); - } - - public static DensityFunction decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return DensityFunction.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static SpongeDensityFunctionTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final DensityFunction parsed = SpongeDensityFunctionTemplate.decode(packEntry, registryAccess); - return new SpongeDensityFunctionTemplate(key, parsed, pack); - } - - - public static final class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - @Nullable private DensityFunction densityFunction; - - public BuilderImpl() { - this.reset(); - } - - @Override - public Function valueExtractor() { - return DensityFunctionTemplate::densityFunction; - } - - @Override - public Builder fromValue(final org.spongepowered.api.world.generation.config.noise.DensityFunction densityFunction) { - this.densityFunction = (DensityFunction) densityFunction; - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - this.densityFunction = SpongeDensityFunctionTemplate.decode(json, SpongeCommon.server().registryAccess()); - return this; - } - - @Override - public Builder reset() { - this.pack = DataPacks.DENSITY_FUNCTION; - this.key = null; - this.densityFunction = null; - return this; - } - - @Override - protected SpongeDensityFunctionTemplate build0() { - Objects.requireNonNull(this.densityFunction, "densityFunction"); - return new SpongeDensityFunctionTemplate(this.key, this.densityFunction, this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseBuilder.java b/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseBuilder.java new file mode 100644 index 00000000000..c622ace73e5 --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseBuilder.java @@ -0,0 +1,82 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.generation.config.noise; + +import net.minecraft.world.level.levelgen.synth.NormalNoise; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.world.generation.config.noise.Noise; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.DoubleStream; + +public final class SpongeNoiseBuilder implements Noise.Builder { + + @Nullable private Integer octave; + @Nullable private List amplitudes; + + public SpongeNoiseBuilder() { + this.reset(); + } + + @Override + public Noise.Builder from(final Noise noise) { + this.octave(noise.octave()).amplitudes(noise.amplitudes()); + return this; + } + + @Override + public Noise.Builder octave(final int octave) { + this.octave = octave; + return this; + } + + @Override + public Noise.Builder amplitudes(final double... amplitudes) { + this.amplitudes = DoubleStream.of(amplitudes).boxed().toList(); + return this; + } + + @Override + public Noise.Builder amplitudes(final List amplitudes) { + this.amplitudes = amplitudes; + return this; + } + + @Override + public Noise.Builder reset() { + this.octave = null; + this.amplitudes = null; + return this; + } + + @Override + public Noise build() { + Objects.requireNonNull(this.octave, "octave"); + Objects.requireNonNull(this.amplitudes, "amplitudes"); + return (Noise) (Object) new NormalNoise.NoiseParameters(this.octave, new ArrayList<>(this.amplitudes)); + } +} diff --git a/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseGeneratorConfigBuilder.java b/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseGeneratorConfigBuilder.java new file mode 100644 index 00000000000..9d209c1707e --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseGeneratorConfigBuilder.java @@ -0,0 +1,169 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.generation.config.noise; + +import net.minecraft.world.level.biome.OverworldBiomeBuilder; +import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockTypes; +import org.spongepowered.api.world.biome.BiomeAttributes; +import org.spongepowered.api.world.generation.config.SurfaceRule; +import org.spongepowered.api.world.generation.config.noise.NoiseConfig; +import org.spongepowered.api.world.generation.config.noise.NoiseConfigs; +import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfig; +import org.spongepowered.api.world.generation.config.noise.NoiseRouter; + +import java.util.List; +import java.util.Objects; + +public final class SpongeNoiseGeneratorConfigBuilder implements NoiseGeneratorConfig.Builder { + + public NoiseConfig noiseConfig; + public BlockState defaultBlock, defaultFluid; + public int seaLevel; + public boolean aquifers, oreVeins, legacyRandomSource, disableMobGeneration; + public SurfaceRule surfaceRule; + private NoiseRouter router; + private List spawnTargets; + + public SpongeNoiseGeneratorConfigBuilder() { + this.reset(); + } + + @Override + public NoiseGeneratorConfig.Builder noiseConfig(final NoiseConfig config) { + this.noiseConfig = Objects.requireNonNull(config, "config"); + return this; + } + + @Override + public NoiseGeneratorConfig.Builder defaultBlock(final BlockState block) { + this.defaultBlock = Objects.requireNonNull(block, "block"); + return this; + } + + @Override + public NoiseGeneratorConfig.Builder defaultFluid(final BlockState fluid) { + this.defaultFluid = Objects.requireNonNull(fluid, "fluid"); + return this; + } + + @Override + public NoiseGeneratorConfig.Builder surfaceRule(SurfaceRule rule) { + this.surfaceRule = rule; + return this; + } + + + @Override + public NoiseGeneratorConfig.Builder seaLevel(final int y) { + this.seaLevel = y; + return this; + } + + @Override + public NoiseGeneratorConfig.Builder mobGeneration(boolean mobGeneration) { + this.disableMobGeneration = !mobGeneration; + return this; + } + + @Override + public NoiseGeneratorConfig.Builder aquifers(final boolean enableAquifers) { + this.aquifers = enableAquifers; + return this; + } + + @Override + public NoiseGeneratorConfig.Builder oreVeins(final boolean enableOreVeins) { + this.oreVeins = enableOreVeins; + return this; + } + + @Override + public NoiseGeneratorConfig.Builder randomSource(boolean useLegacyRandomSource) { + this.legacyRandomSource = useLegacyRandomSource; + return this; + } + + @Override + public NoiseGeneratorConfig.Builder noiseRouter(final NoiseRouter router) { + this.router = router; + return this; + } + + @Override + public NoiseGeneratorConfig.Builder spawnTargets(final List spawnTargets) { + this.spawnTargets = spawnTargets; + return this; + } + + @Override + public NoiseGeneratorConfig.Builder reset() { + this.noiseConfig = NoiseConfigs.OVERWORLD.get(); + this.defaultBlock = BlockTypes.STONE.get().defaultState(); + this.defaultFluid = BlockTypes.WATER.get().defaultState(); + this.surfaceRule = SurfaceRule.overworld(); + this.seaLevel = 63; + this.aquifers = false; + this.oreVeins = false; + this.legacyRandomSource = false; + this.router = null; + this.spawnTargets = (List) new OverworldBiomeBuilder().spawnTarget(); + return this; + } + + @Override + public NoiseGeneratorConfig.Builder from(final NoiseGeneratorConfig value) { + this.noiseConfig = value.noiseConfig(); + this.defaultBlock = value.defaultBlock(); + this.defaultFluid = value.defaultFluid(); + this.surfaceRule = value.surfaceRule(); + this.seaLevel = value.seaLevel(); + this.aquifers = value.aquifers(); + this.oreVeins = value.oreVeins(); + this.legacyRandomSource = value.legacyRandomSource(); + this.router = value.noiseRouter(); + this.spawnTargets = value.spawnTargets(); + return this; + } + + @Override + public NoiseGeneratorConfig build() { + final NoiseGeneratorSettings settings = new NoiseGeneratorSettings( + (net.minecraft.world.level.levelgen.NoiseSettings) (Object) this.noiseConfig, + (net.minecraft.world.level.block.state.BlockState) this.defaultBlock, + (net.minecraft.world.level.block.state.BlockState) this.defaultFluid, + (net.minecraft.world.level.levelgen.NoiseRouter) (Object) Objects.requireNonNull(this.router, "router"), + (net.minecraft.world.level.levelgen.SurfaceRules.RuleSource) this.surfaceRule, + (List) this.spawnTargets, + this.seaLevel, + this.disableMobGeneration, + this.aquifers, + this.oreVeins, + this.legacyRandomSource + ); + return (NoiseGeneratorConfig) (Object) settings; + } +} diff --git a/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseGeneratorConfigTemplate.java b/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseGeneratorConfigTemplate.java deleted file mode 100644 index cb7707232b1..00000000000 --- a/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseGeneratorConfigTemplate.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.generation.config.noise; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.RegistryOps; -import net.minecraft.world.level.biome.OverworldBiomeBuilder; -import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.block.BlockState; -import org.spongepowered.api.block.BlockTypes; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.world.biome.BiomeAttributes; -import org.spongepowered.api.world.generation.config.SurfaceRule; -import org.spongepowered.api.world.generation.config.noise.NoiseConfig; -import org.spongepowered.api.world.generation.config.noise.NoiseConfigs; -import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfig; -import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfigTemplate; -import org.spongepowered.api.world.generation.config.noise.NoiseGeneratorConfigs; -import org.spongepowered.api.world.generation.config.noise.NoiseRouter; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.List; -import java.util.Objects; -import java.util.function.Function; - -public record SpongeNoiseGeneratorConfigTemplate(ResourceKey key, NoiseGeneratorSettings representedSettings, DataPack pack) implements NoiseGeneratorConfigTemplate { - - @Override - public NoiseGeneratorConfig config() { - return (NoiseGeneratorConfig) (Object) this.representedSettings; - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeNoiseGeneratorConfigTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized NoiseGeneratorConfig:\n" + serialized, e); - } - } - - public static JsonElement encode(final NoiseGeneratorConfigTemplate template, final RegistryAccess registryAccess) { - return NoiseGeneratorSettings.DIRECT_CODEC.encodeStart(JsonOps.INSTANCE, (NoiseGeneratorSettings) (Object) template.config()).getOrThrow(); - } - - public static NoiseGeneratorSettings decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return NoiseGeneratorSettings.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static NoiseGeneratorConfigTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final NoiseGeneratorSettings parsed = SpongeNoiseGeneratorConfigTemplate.decode(packEntry, registryAccess); - return new SpongeNoiseGeneratorConfigTemplate(key, parsed, pack); - } - - - public static final class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - public NoiseConfig noiseConfig; - public BlockState defaultBlock, defaultFluid; - public int seaLevel; - public boolean aquifers, oreVeins, legacyRandomSource, disableMobGeneration; - public SurfaceRule surfaceRule; - private NoiseRouter router; - private List spawnTargets; - - public BuilderImpl() { - this.reset(); - } - - @Override - public Function valueExtractor() { - return NoiseGeneratorConfigTemplate::config; - } - - @Override - public Builder noiseConfig(final NoiseConfig config) { - this.noiseConfig = Objects.requireNonNull(config, "config"); - return this; - } - - @Override - public Builder defaultBlock(final BlockState block) { - this.defaultBlock = Objects.requireNonNull(block, "block"); - return this; - } - - @Override - public Builder defaultFluid(final BlockState fluid) { - this.defaultFluid = Objects.requireNonNull(fluid, "fluid"); - return this; - } - - @Override - public Builder surfaceRule(SurfaceRule rule) { - this.surfaceRule = rule; - return this; - } - - - @Override - public Builder seaLevel(final int y) { - this.seaLevel = y; - return this; - } - - @Override - public Builder mobGeneration(boolean mobGeneration) { - this.disableMobGeneration = !mobGeneration; - return this; - } - - @Override - public Builder aquifers(final boolean enableAquifers) { - this.aquifers = enableAquifers; - return this; - } - - @Override - public Builder oreVeins(final boolean enableOreVeins) { - this.oreVeins = enableOreVeins; - return this; - } - - @Override - public Builder randomSource(boolean useLegacyRandomSource) { - this.legacyRandomSource = useLegacyRandomSource; - return this; - } - - @Override - public Builder noiseRouter(final NoiseRouter router) { - this.router = router; - return this; - } - - @Override - public Builder spawnTargets(final List spawnTargets) { - this.spawnTargets = spawnTargets; - return this; - } - - @Override - public Builder reset() { - this.noiseConfig = NoiseConfigs.OVERWORLD.get(); - this.defaultBlock = BlockTypes.STONE.get().defaultState(); - this.defaultFluid = BlockTypes.WATER.get().defaultState(); - this.surfaceRule = SurfaceRule.overworld(); - this.seaLevel = 63; - this.aquifers = false; - this.oreVeins = false; - this.legacyRandomSource = false; - this.router = NoiseGeneratorConfigs.OVERWORLD.get().noiseRouter(); - this.spawnTargets = (List) new OverworldBiomeBuilder().spawnTarget();; - this.pack = DataPacks.NOISE_GENERATOR_CONFIG; - this.key = null; - return this; - } - - @Override - public Builder fromValue(final NoiseGeneratorConfig value) { - this.noiseConfig = value.noiseConfig(); - this.defaultBlock = value.defaultBlock(); - this.defaultFluid = value.defaultFluid(); - this.surfaceRule = value.surfaceRule(); - this.seaLevel = value.seaLevel(); - this.aquifers = value.aquifers(); - this.legacyRandomSource = value.legacyRandomSource(); - return this; - } - - @Override - public Builder fromDataPack(final DataView datapack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(datapack)); - final NoiseGeneratorSettings decoded = SpongeNoiseGeneratorConfigTemplate.decode(json, SpongeCommon.server().registryAccess()); - return this.fromValue((NoiseGeneratorConfig) (Object) decoded); - } - - @Override - protected NoiseGeneratorConfigTemplate build0() { - final NoiseGeneratorSettings settings = new NoiseGeneratorSettings( - (net.minecraft.world.level.levelgen.NoiseSettings) (Object) this.noiseConfig, - (net.minecraft.world.level.block.state.BlockState) this.defaultBlock, - (net.minecraft.world.level.block.state.BlockState) this.defaultFluid, - (net.minecraft.world.level.levelgen.NoiseRouter) (Object) this.router, - (net.minecraft.world.level.levelgen.SurfaceRules.RuleSource) this.surfaceRule, - (List) this.spawnTargets, - this.seaLevel, - this.disableMobGeneration, - this.aquifers, - this.oreVeins, - this.legacyRandomSource - ); - return new SpongeNoiseGeneratorConfigTemplate(this.key, settings, this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseTemplate.java b/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseTemplate.java deleted file mode 100644 index 57e4ab827cd..00000000000 --- a/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeNoiseTemplate.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.generation.config.noise; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.RegistryOps; -import net.minecraft.world.level.levelgen.synth.NormalNoise; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.world.generation.config.noise.Noise; -import org.spongepowered.api.world.generation.config.noise.NoiseTemplate; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.function.Function; -import java.util.stream.DoubleStream; - -public record SpongeNoiseTemplate(ResourceKey key, NormalNoise.NoiseParameters noiseParameters, DataPack pack) implements NoiseTemplate { - - @Override - public Noise noise() { - return (Noise) (Object) this.noiseParameters; - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeNoiseTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Noise:\n" + serialized, e); - } - } - - public static JsonElement encode(final NoiseTemplate template, final RegistryAccess registryAccess) { - return NormalNoise.NoiseParameters.DIRECT_CODEC.encodeStart(JsonOps.INSTANCE, (NormalNoise.NoiseParameters) (Object) template.noise()).getOrThrow(); - } - - public static NormalNoise.NoiseParameters decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return NormalNoise.NoiseParameters.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static SpongeNoiseTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final NormalNoise.NoiseParameters parsed = SpongeNoiseTemplate.decode(packEntry, registryAccess); - return new SpongeNoiseTemplate(key, parsed, pack); - } - - - public static final class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - @Nullable private Integer octave; - @Nullable private List amplitudes; - - public BuilderImpl() { - this.reset(); - } - - @Override - public Builder fromValue(final Noise noise) { - this.octave(noise.octave()).amplitudes(noise.amplitudes()); - return this; - } - - @Override - public Builder octave(final int octave) { - this.octave = octave; - return this; - } - - @Override - public Builder amplitudes(final double... amplitudes) { - this.amplitudes = DoubleStream.of(amplitudes).boxed().toList(); - return this; - } - - @Override - public Builder amplitudes(final List amplitudes) { - this.amplitudes = amplitudes; - return this; - } - - @Override - public Builder reset() { - this.octave = null; - this.amplitudes = null; - this.pack = DataPacks.NOISE; - this.key = null; - return this; - } - - @Override - public Builder fromDataPack(final DataView datapack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(datapack)); - return this.fromValue((Noise) (Object) SpongeNoiseTemplate.decode(json, SpongeCommon.server().registryAccess())); - } - - @Override - public Function valueExtractor() { - return NoiseTemplate::noise; - } - - @Override - protected SpongeNoiseTemplate build0() { - Objects.requireNonNull(this.octave, "octave"); - Objects.requireNonNull(this.amplitudes, "amplitudes"); - final NormalNoise.NoiseParameters params = new NormalNoise.NoiseParameters(this.octave, new ArrayList<>(this.amplitudes)); - return new SpongeNoiseTemplate(this.key, params, this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeSurfaceRulesFactory.java b/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeSurfaceRulesFactory.java index 1b66e7ef852..fef00b3ea2b 100644 --- a/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeSurfaceRulesFactory.java +++ b/src/main/java/org/spongepowered/common/world/generation/config/noise/SpongeSurfaceRulesFactory.java @@ -26,7 +26,7 @@ import net.minecraft.core.registries.Registries; import net.minecraft.data.worldgen.SurfaceRuleData; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.levelgen.SurfaceRules; import net.minecraft.world.level.levelgen.VerticalAnchor; import net.minecraft.world.level.levelgen.placement.CaveSurface; @@ -36,6 +36,7 @@ import org.spongepowered.api.world.biome.Biome; import org.spongepowered.api.world.generation.config.SurfaceRule; import org.spongepowered.api.world.generation.config.noise.Noise; +import org.spongepowered.common.SpongeCommon; import java.util.Arrays; import java.util.List; @@ -44,17 +45,17 @@ public final class SpongeSurfaceRulesFactory implements SurfaceRule.Factory { @Override public SurfaceRule overworld() { - return (SurfaceRule) SurfaceRuleData.overworld(); + return (SurfaceRule) SurfaceRuleData.overworld(SpongeCommon.vanillaRegistry(Registries.BIOME)); } @Override public SurfaceRule overworldLike(final boolean nearSurface, final boolean bedrockRoof, final boolean bedrockFloor) { - return (SurfaceRule) SurfaceRuleData.overworldLike(nearSurface, bedrockRoof, bedrockFloor); + return (SurfaceRule) SurfaceRuleData.overworldLike(SpongeCommon.vanillaRegistry(Registries.BIOME), nearSurface, bedrockRoof, bedrockFloor); } @Override public SurfaceRule nether() { - return (SurfaceRule) SurfaceRuleData.nether(); + return (SurfaceRule) SurfaceRuleData.nether(SpongeCommon.vanillaRegistry(Registries.BIOME)); } @Override @@ -109,9 +110,9 @@ public SurfaceRule.Condition liquidDepthFromSurface(final int offset, final int @Override public SurfaceRule.Condition biome(final List> biomes) { final var stream = biomes.stream() - .map(r -> net.minecraft.resources.ResourceKey.create(Registries.BIOME, ((ResourceLocation) (Object) r.location()))); + .map(r -> net.minecraft.resources.ResourceKey.create(Registries.BIOME, ((Identifier) (Object) r.location()))); final net.minecraft.resources.ResourceKey[] keys = stream.toArray(net.minecraft.resources.ResourceKey[]::new); - return (SurfaceRule.Condition) SurfaceRules.isBiome(keys); + return (SurfaceRule.Condition) SurfaceRules.isBiome(SpongeCommon.vanillaRegistry(Registries.BIOME), keys); } @Override @@ -193,8 +194,8 @@ public SurfaceRule.Condition surfaceAbove(final SurfaceRule.VerticalAnchor ancho @Override public SurfaceRule.Condition noiseThreshold(final RegistryReference noise, final double min, final double max) { - final net.minecraft.resources.ResourceKey key = net.minecraft.resources.ResourceKey.create(Registries.NOISE, ((ResourceLocation) (Object) noise.location())); - return (SurfaceRule.Condition) SurfaceRules.noiseCondition(key, min, max); + final net.minecraft.resources.ResourceKey key = net.minecraft.resources.ResourceKey.create(Registries.NOISE, ((Identifier) (Object) noise.location())); + return (SurfaceRule.Condition) SurfaceRules.noiseCondition2d(key, min, max); } // Anchors diff --git a/src/main/java/org/spongepowered/common/world/generation/feature/SpongeFeatureBuilder.java b/src/main/java/org/spongepowered/common/world/generation/feature/SpongeFeatureBuilder.java new file mode 100644 index 00000000000..316ffd292a8 --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/generation/feature/SpongeFeatureBuilder.java @@ -0,0 +1,69 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.generation.feature; + +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; +import org.spongepowered.api.world.generation.feature.Feature; +import org.spongepowered.api.world.generation.feature.FeatureType; + +import java.util.Objects; + +public final class SpongeFeatureBuilder implements Feature.Builder { + + private net.minecraft.world.level.levelgen.feature.Feature type; + private FeatureConfiguration config; + + public SpongeFeatureBuilder() { + this.reset(); + } + + @Override + public Feature.Builder type(final FeatureType type) { + this.type = (net.minecraft.world.level.levelgen.feature.Feature) type; + return this; + } + + @Override + public Feature.Builder reset() { + this.type = null; + this.config = null; + return this; + } + + @Override + public Feature.Builder from(final org.spongepowered.api.world.generation.feature.Feature feature) { + this.type(feature.type()); + this.config = ((ConfiguredFeature) (Object) feature).config(); + return this; + } + + @Override + public Feature build() { + Objects.requireNonNull(this.type, "config"); + Objects.requireNonNull(this.config, "config"); + return (Feature) (Object) new ConfiguredFeature<>((net.minecraft.world.level.levelgen.feature.Feature) this.type, this.config); + } +} diff --git a/src/main/java/org/spongepowered/common/world/generation/feature/SpongeFeatureTemplate.java b/src/main/java/org/spongepowered/common/world/generation/feature/SpongeFeatureTemplate.java deleted file mode 100644 index 514c33a6f2b..00000000000 --- a/src/main/java/org/spongepowered/common/world/generation/feature/SpongeFeatureTemplate.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.generation.feature; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.RegistryOps; -import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; -import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.world.generation.feature.Feature; -import org.spongepowered.api.world.generation.feature.FeatureTemplate; -import org.spongepowered.api.world.generation.feature.FeatureType; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.Objects; -import java.util.function.Function; - -public record SpongeFeatureTemplate(ResourceKey key, ConfiguredFeature representedFeature, DataPack pack) implements FeatureTemplate { - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeFeatureTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Feature:\n" + serialized, e); - } - } - - @Override - public org.spongepowered.api.world.generation.feature.Feature feature() { - return (org.spongepowered.api.world.generation.feature.Feature) (Object) this.representedFeature; - } - - public static JsonElement encode(final FeatureTemplate template, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return ConfiguredFeature.DIRECT_CODEC.encodeStart(ops, (ConfiguredFeature) (Object) template.feature()).getOrThrow(); - } - - public static ConfiguredFeature decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return ConfiguredFeature.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static SpongeFeatureTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final ConfiguredFeature parsed = SpongeFeatureTemplate.decode(packEntry, registryAccess); - return new SpongeFeatureTemplate(key, parsed, pack); - } - - public static class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - private net.minecraft.world.level.levelgen.feature.Feature type; - private FeatureConfiguration config; - - public BuilderImpl() { - this.reset(); - } - - @Override - public Function valueExtractor() { - return FeatureTemplate::feature; - } - - @Override - public Builder type(final FeatureType type) { - this.type = (net.minecraft.world.level.levelgen.feature.Feature) type; - return this; - } - - @Override - public Builder reset() { - this.key = null; - this.type = null; - this.config = null; - this.pack = DataPacks.FEATURE; - return this; - } - - @Override - public Builder fromValue(final org.spongepowered.api.world.generation.feature.Feature feature) { - this.type(feature.type()); - this.config = ((ConfiguredFeature) (Object) feature).config(); - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - final ConfiguredFeature feature = SpongeFeatureTemplate.decode(json, SpongeCommon.server().registryAccess()); - this.fromValue((Feature) (Object) feature); - return this; - } - - @Override - protected FeatureTemplate build0() { - Objects.requireNonNull(this.type, "config"); - Objects.requireNonNull(this.config, "config"); - final ConfiguredFeature feature = new ConfiguredFeature<>((net.minecraft.world.level.levelgen.feature.Feature) this.type, this.config); - return new SpongeFeatureTemplate(this.key, feature, this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/generation/feature/SpongePlacedFeatureBuilder.java b/src/main/java/org/spongepowered/common/world/generation/feature/SpongePlacedFeatureBuilder.java new file mode 100644 index 00000000000..e5d6861a7cd --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/generation/feature/SpongePlacedFeatureBuilder.java @@ -0,0 +1,76 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.generation.feature; + +import net.minecraft.core.Holder; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.placement.PlacementModifier; +import org.spongepowered.api.world.generation.feature.Feature; +import org.spongepowered.api.world.generation.feature.PlacedFeature; + +import java.util.ArrayList; +import java.util.List; + +public final class SpongePlacedFeatureBuilder implements PlacedFeature.Builder { + + private Holder> feature; + private List modifiers = new ArrayList<>(); + + public SpongePlacedFeatureBuilder() { + this.reset(); + } + + @Override + public PlacedFeature.Builder reset() { + this.feature = null; + this.modifiers.clear(); + return this; + } + + @Override + public PlacedFeature.Builder from(final PlacedFeature feature) { + this.feature(feature.feature()); + this.modifiers.clear(); + this.modifiers.addAll((List) feature.placementModifiers()); + return this; + } + + @Override + public PlacedFeature.Builder feature(final Feature feature) { + this.feature = Holder.direct((ConfiguredFeature) (Object) feature); + return this; + } + + @Override + public PlacedFeature.Builder addModifier(final org.spongepowered.api.world.generation.feature.PlacementModifier modifier) { + this.modifiers.add((net.minecraft.world.level.levelgen.placement.PlacementModifier) modifier); + return this; + } + + @Override + public PlacedFeature build() { + return (PlacedFeature) (Object) new net.minecraft.world.level.levelgen.placement.PlacedFeature(this.feature, this.modifiers); + } +} diff --git a/src/main/java/org/spongepowered/common/world/generation/feature/SpongePlacedFeatureTemplate.java b/src/main/java/org/spongepowered/common/world/generation/feature/SpongePlacedFeatureTemplate.java deleted file mode 100644 index 637c7386973..00000000000 --- a/src/main/java/org/spongepowered/common/world/generation/feature/SpongePlacedFeatureTemplate.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.generation.feature; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.RegistryOps; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.data.persistence.Queries; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.world.generation.feature.Feature; -import org.spongepowered.api.world.generation.feature.FeatureTemplate; -import org.spongepowered.api.world.generation.feature.PlacedFeature; -import org.spongepowered.api.world.generation.feature.PlacedFeatureTemplate; -import org.spongepowered.api.world.generation.feature.PlacementModifier; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -public record SpongePlacedFeatureTemplate(ResourceKey key, net.minecraft.world.level.levelgen.placement.PlacedFeature represented, DataPack pack) implements PlacedFeatureTemplate { - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongePlacedFeatureTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - final DataContainer container = DataFormats.JSON.get().read(serialized.toString()); - container.set(Queries.CONTENT_VERSION, this.contentVersion()); - return container; - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Placed Feature:\n" + serialized, e); - } - } - - @Override - public PlacedFeature feature() { - return (PlacedFeature) (Object) this.represented; - } - - public static JsonElement encode(final PlacedFeatureTemplate template, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return net.minecraft.world.level.levelgen.placement.PlacedFeature.DIRECT_CODEC.encodeStart(ops, (net.minecraft.world.level.levelgen.placement.PlacedFeature) (Object) template.feature()).getOrThrow(); - } - - public static net.minecraft.world.level.levelgen.placement.PlacedFeature decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return net.minecraft.world.level.levelgen.placement.PlacedFeature.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static PlacedFeatureTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final net.minecraft.world.level.levelgen.placement.PlacedFeature parsed = SpongePlacedFeatureTemplate.decode(packEntry, registryAccess); - return new SpongePlacedFeatureTemplate(key, parsed, pack); - } - - public static class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - private Holder> feature; - private List modifiers = new ArrayList<>(); - - public BuilderImpl() { - this.reset(); - } - - @Override - public Function valueExtractor() { - return PlacedFeatureTemplate::feature; - } - - @Override - public Builder reset() { - this.key = null; - this.feature = null; - this.modifiers.clear(); - this.pack = DataPacks.PLACED_FEATURE; - return this; - } - - @Override - public Builder fromValue(final PlacedFeature feature) { - this.feature(feature.feature()); - this.modifiers.clear(); - this.modifiers.addAll((List) feature.placementModifiers()); - return this; - } - - @Override - public Builder feature(final Feature feature) { - final Registry> registry = SpongeCommon.vanillaRegistry(Registries.CONFIGURED_FEATURE); - final ResourceLocation key = registry.getKey((ConfiguredFeature) (Object) feature); - if (key == null) { - this.feature = Holder.direct((ConfiguredFeature) (Object) feature); - } else { - this.feature = registry.getHolderOrThrow(net.minecraft.resources.ResourceKey.create(Registries.CONFIGURED_FEATURE, key)); - } - return this; - } - - @Override - public Builder feature(final FeatureTemplate feature) { - final Registry> registry = SpongeCommon.vanillaRegistry(Registries.CONFIGURED_FEATURE); - final net.minecraft.resources.ResourceKey> key = - net.minecraft.resources.ResourceKey.create(Registries.CONFIGURED_FEATURE, ((ResourceLocation) (Object) feature.key())); - this.feature = Holder.Reference.createStandAlone(registry.asLookup(), key); - return this; - } - - @Override - public Builder addModifier(final PlacementModifier modifier) { - this.modifiers.add((net.minecraft.world.level.levelgen.placement.PlacementModifier) modifier); - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - final net.minecraft.world.level.levelgen.placement.PlacedFeature feature = SpongePlacedFeatureTemplate.decode(json, SpongeCommon.server().registryAccess()); - this.fromValue((PlacedFeature) (Object) feature); - return this; - } - - @Override - protected PlacedFeatureTemplate build0() { - var feature = new net.minecraft.world.level.levelgen.placement.PlacedFeature(this.feature, this.modifiers); - return new SpongePlacedFeatureTemplate(this.key, feature, this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/generation/structure/SpongeSchematicTemplate.java b/src/main/java/org/spongepowered/common/world/generation/structure/SpongeSchematicTemplate.java deleted file mode 100644 index 924ef197561..00000000000 --- a/src/main/java/org/spongepowered/common/world/generation/structure/SpongeSchematicTemplate.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.generation.structure; - -import net.minecraft.core.RegistryAccess; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.world.generation.structure.SchematicTemplate; -import org.spongepowered.api.world.schematic.Schematic; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.data.persistence.NBTTranslator; -import org.spongepowered.common.util.AbstractResourceKeyedBuilder; - -import java.util.Objects; - -public record SpongeSchematicTemplate(ResourceKey key, StructureTemplate nbtStructure, DataPack pack) implements SchematicTemplate { - - @Override - public Schematic schematic() { - return (Schematic) this.nbtStructure; - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final CompoundTag serialized = SpongeSchematicTemplate.encode(this, SpongeCommon.server().registryAccess()); - return NBTTranslator.INSTANCE.translateFrom(serialized); - } - - public static CompoundTag encode(final SchematicTemplate template, final RegistryAccess registryAccess) { - return ((SpongeSchematicTemplate) template).nbtStructure.save(new CompoundTag()); - } - - public static StructureTemplate decode(final CompoundTag nbt, final RegistryAccess registryAccess) { - return SpongeCommon.server().getStructureManager().readStructure(nbt); - } - - public static SpongeSchematicTemplate decode(final DataPack pack, final ResourceKey key, final CompoundTag packEntry, final RegistryAccess registryAccess) { - final StructureTemplate parsed = SpongeSchematicTemplate.decode(packEntry, registryAccess); - return new SpongeSchematicTemplate(key, parsed, pack); - } - - public static final class BuilderImpl extends AbstractResourceKeyedBuilder implements Builder { - - private DataPack pack = DataPacks.SCHEMATIC; - @Nullable private StructureTemplate nbtStructure; - - public BuilderImpl() { - this.reset(); - } - - @Override - public Builder from(final SchematicTemplate value) { - this.nbtStructure = ((SpongeSchematicTemplate) value).nbtStructure; - return this; - } - - @Override - public Builder reset() { - this.pack = DataPacks.SCHEMATIC; - this.nbtStructure = null; - return this; - } - - @Override - public Builder pack(final DataPack pack) { - this.pack = pack; - return this; - } - - @Override - protected SpongeSchematicTemplate build0() { - Objects.requireNonNull(this.nbtStructure, "structure"); - return new SpongeSchematicTemplate(this.key, this.nbtStructure, this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructurePlacementBuilder.java b/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructurePlacementBuilder.java index 4c8d9b61c12..0f4fe1c02e4 100644 --- a/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructurePlacementBuilder.java +++ b/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructurePlacementBuilder.java @@ -28,7 +28,6 @@ import net.minecraft.core.Registry; import net.minecraft.core.Vec3i; import net.minecraft.core.registries.Registries; -import net.minecraft.tags.TagKey; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; @@ -154,12 +153,14 @@ public ConcentricRings count(final int count) { @Override public ConcentricRings preferredBiomes(final Tag preferredBiomes) { final Registry registry = SpongeCommon.vanillaRegistry(Registries.BIOME); - this.preferredBiomes = registry.getOrCreateTag((TagKey) (Object) preferredBiomes); + // TODO - Snapshot 24w34a +// this.preferredBiomes = registry.getOrCreateTag((TagKey) (Object) preferredBiomes); return this; } // Building + @SuppressWarnings("deprecation") @Override public StructurePlacement build() { final FrequencyReductionMethod method = FrequencyReductionMethod.DEFAULT; // TODO expose other FrequencyReductionMethods? diff --git a/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructureSetBuilder.java b/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructureSetBuilder.java new file mode 100644 index 00000000000..8991ae7add7 --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructureSetBuilder.java @@ -0,0 +1,80 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.generation.structure; + +import net.minecraft.core.Holder; +import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.world.generation.structure.Structure; +import org.spongepowered.api.world.generation.structure.StructureSet; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public final class SpongeStructureSetBuilder implements StructureSet.Builder { + + @Nullable + private StructurePlacement placement; + private List structureSelectionEntries = new ArrayList<>(); + + public SpongeStructureSetBuilder() { + this.reset(); + } + + @Override + public StructureSet.Builder from(final org.spongepowered.api.world.generation.structure.StructureSet structureSet) { + var mcSet = (net.minecraft.world.level.levelgen.structure.StructureSet) (Object) structureSet; + this.structureSelectionEntries = mcSet.structures(); + this.placement = mcSet.placement(); + return this; + } + + @Override + public StructureSet.Builder placement(final org.spongepowered.api.world.generation.structure.StructurePlacement placement) { + this.placement = (StructurePlacement) placement; + return this; + } + + @Override + public StructureSet.Builder add(final Structure structure, final int weight) { + final var mcStructure = (net.minecraft.world.level.levelgen.structure.Structure) structure; + this.structureSelectionEntries.add(new net.minecraft.world.level.levelgen.structure.StructureSet.StructureSelectionEntry(Holder.direct(mcStructure), weight)); + return this; + } + + @Override + public StructureSet.Builder reset() { + this.placement = null; + this.structureSelectionEntries = new ArrayList<>(); + return this; + } + + @Override + public StructureSet build() { + Objects.requireNonNull(this.placement, "placement"); + return (StructureSet) (Object) new net.minecraft.world.level.levelgen.structure.StructureSet(this.structureSelectionEntries, this.placement); + } +} diff --git a/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructureSetTemplate.java b/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructureSetTemplate.java deleted file mode 100644 index e2b13b26b98..00000000000 --- a/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructureSetTemplate.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.generation.structure; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.RegistryOps; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.world.generation.structure.Structure; -import org.spongepowered.api.world.generation.structure.StructureSetTemplate; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.function.Function; - -public record SpongeStructureSetTemplate(ResourceKey key, StructureSet representedStructureSet, DataPack pack) implements StructureSetTemplate { - - @Override - public org.spongepowered.api.world.generation.structure.StructureSet structureSet() { - return (org.spongepowered.api.world.generation.structure.StructureSet) (Object) this.representedStructureSet; - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeStructureSetTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Structure:\n" + serialized, e); - } - } - - public static JsonElement encode(final StructureSetTemplate template, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return StructureSet.DIRECT_CODEC.encodeStart(ops, (StructureSet) (Object) template.structureSet()).getOrThrow(); - } - - public static StructureSet decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return StructureSet.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static SpongeStructureSetTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final StructureSet parsed = SpongeStructureSetTemplate.decode(packEntry, registryAccess); - return new SpongeStructureSetTemplate(key, parsed, pack); - } - - public static final class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - @Nullable private StructurePlacement placement; - private List structureSelectionEntries = new ArrayList<>(); - - public BuilderImpl() { - this.reset(); - } - - @Override - public Function valueExtractor() { - return StructureSetTemplate::structureSet; - } - - @Override - public Builder fromValue(final org.spongepowered.api.world.generation.structure.StructureSet structureSet) { - var mcSet = (StructureSet) (Object) structureSet; - this.structureSelectionEntries = mcSet.structures(); - this.placement = mcSet.placement(); - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - final StructureSet decoded = SpongeStructureSetTemplate.decode(json, SpongeCommon.server().registryAccess()); - this.structureSelectionEntries = decoded.structures(); - this.placement = decoded.placement(); - return this; - } - - @Override - public Builder placement(final org.spongepowered.api.world.generation.structure.StructurePlacement placement) { - this.placement = (StructurePlacement) placement; - return this; - } - - @Override - public Builder add(final Structure structure, final int weight) { - final Registry registry = SpongeCommon.vanillaRegistry(Registries.STRUCTURE); - final var mcStructure = (net.minecraft.world.level.levelgen.structure.Structure) structure; - Holder holder; - try { - holder = registry.createIntrusiveHolder(mcStructure); - } catch (Exception e) { - holder = Holder.direct(mcStructure); - } - this.structureSelectionEntries.add(new StructureSet.StructureSelectionEntry(holder, weight)); - return this; - } - - @Override - public Builder reset() { - super.reset(); - this.pack = DataPacks.STRUCTURE_SET; - this.placement = null; - this.structureSelectionEntries = new ArrayList<>(); - return this; - } - - @Override - protected SpongeStructureSetTemplate build0() { - Objects.requireNonNull(this.placement, "placement"); - return new SpongeStructureSetTemplate(this.key, new StructureSet(this.structureSelectionEntries, this.placement), this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructureTemplate.java b/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructureTemplate.java deleted file mode 100644 index 61894fca865..00000000000 --- a/src/main/java/org/spongepowered/common/world/generation/structure/SpongeStructureTemplate.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.generation.structure; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.RegistryOps; -import net.minecraft.world.level.levelgen.structure.Structure; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.world.generation.structure.StructureTemplate; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.Objects; -import java.util.function.Function; - -public record SpongeStructureTemplate(ResourceKey key, Structure representedStructure, DataPack pack) implements StructureTemplate { - - @Override - public org.spongepowered.api.world.generation.structure.Structure structure() { - return (org.spongepowered.api.world.generation.structure.Structure) this.representedStructure; - } - - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeStructureTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Structure:\n" + serialized, e); - } - } - - public static JsonElement encode(final StructureTemplate template, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return Structure.DIRECT_CODEC.encodeStart(ops, (Structure) template.structure()).getOrThrow(); - } - - public static net.minecraft.world.level.levelgen.structure.Structure decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return Structure.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static SpongeStructureTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final Structure parsed = SpongeStructureTemplate.decode(packEntry, registryAccess); - return new SpongeStructureTemplate(key, parsed, pack); - } - - public static final class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - @Nullable private Structure structure; - - public BuilderImpl() { - this.reset(); - } - - @Override - public Function valueExtractor() { - return StructureTemplate::structure; - } - - @Override - public Builder fromValue(final org.spongepowered.api.world.generation.structure.Structure structure) { - this.structure = (Structure) structure; - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - this.structure = SpongeStructureTemplate.decode(json, SpongeCommon.server().registryAccess()); - return this; - } - - @Override - public Builder reset() { - this.key = null; - this.pack = DataPacks.STRUCTURE; - this.structure = null; - return this; - } - - @Override - protected SpongeStructureTemplate build0() { - Objects.requireNonNull(this.structure, "Structure"); - return new SpongeStructureTemplate(this.key, this.structure, this.pack); - } - } -} diff --git a/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeJigsawPoolBuilder.java b/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeJigsawPoolBuilder.java new file mode 100644 index 00000000000..4db93fa2b70 --- /dev/null +++ b/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeJigsawPoolBuilder.java @@ -0,0 +1,94 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.world.generation.structure.jigsaw; + +import com.mojang.datafixers.util.Pair; +import net.minecraft.core.Holder; +import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; +import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.registry.RegistryReference; +import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPool; +import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPoolElement; +import org.spongepowered.common.accessor.world.level.levelgen.structure.pools.StructureTemplatePoolAccessor; + +import java.util.ArrayList; +import java.util.List; + +public final class SpongeJigsawPoolBuilder implements JigsawPool.Builder { + + private Holder fallback; + private List> templates; + + public SpongeJigsawPoolBuilder() { + this.reset(); + } + + @Override + public JigsawPool.Builder from(final JigsawPool StructureTemplatePool) { + var mcPool = (StructureTemplatePool) StructureTemplatePool; + this.fallback = mcPool.getFallback(); + this.templates = new ArrayList<>(((StructureTemplatePoolAccessor) mcPool).accessor$rawTemplates()); + return this; + } + + @Override + public JigsawPool.Builder add(final JigsawPoolElement element, final int weight) { + this.templates.add(Pair.of((StructurePoolElement) element, weight)); + return this; + } + + @Override + public JigsawPool.Builder name(final ResourceKey name) { +// TODO this.name = (Identifier) (Object) name; + return this; + } + + @Override + public JigsawPool.Builder fallback(final RegistryReference fallback) { +// TODO this.fallback = (Identifier) (Object) fallback.location(); + return this; + } + + @Override + public JigsawPool.Builder fallback(final JigsawPool fallback) { +// TODO this.fallback = (Identifier) (Object) fallback.key(); + return this; + } + + @Override + public JigsawPool.Builder reset() { + // TODO this.name = null; + // TODO this.fallback = new Identifier("empty"); + this.templates = new ArrayList<>(); + return this; + } + + @Override + public JigsawPool build() { + // TODO Objects.requireNonNull(this.name, "name"); + return (JigsawPool) new StructureTemplatePool(this.fallback, this.templates); + } +} diff --git a/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeJigsawPoolTemplate.java b/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeJigsawPoolTemplate.java deleted file mode 100644 index a6011f2a6cd..00000000000 --- a/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeJigsawPoolTemplate.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.world.generation.structure.jigsaw; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.Holder; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.RegistryOps; -import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.data.persistence.DataContainer; -import org.spongepowered.api.data.persistence.DataFormats; -import org.spongepowered.api.data.persistence.DataView; -import org.spongepowered.api.datapack.DataPack; -import org.spongepowered.api.datapack.DataPacks; -import org.spongepowered.api.registry.RegistryReference; -import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPool; -import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPoolElement; -import org.spongepowered.api.world.generation.structure.jigsaw.JigsawPoolTemplate; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.accessor.world.level.levelgen.structure.pools.StructureTemplatePoolAccessor; -import org.spongepowered.common.util.AbstractDataPackEntryBuilder; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -public record SpongeJigsawPoolTemplate(ResourceKey key, StructureTemplatePool representedJigsawPool, DataPack pack) implements JigsawPoolTemplate { - - @Override - public JigsawPool jigsawPool() { - return (JigsawPool) this.representedJigsawPool; - } - @Override - public int contentVersion() { - return 0; - } - - @Override - public DataContainer toContainer() { - final JsonElement serialized = SpongeJigsawPoolTemplate.encode(this, SpongeCommon.server().registryAccess()); - try { - return DataFormats.JSON.get().read(serialized.toString()); - } catch (IOException e) { - throw new IllegalStateException("Could not read deserialized Structure:\n" + serialized, e); - } - } - - public static JsonElement encode(final JigsawPoolTemplate template, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return StructureTemplatePool.DIRECT_CODEC.encodeStart(ops, (StructureTemplatePool) template.jigsawPool()).getOrThrow(); - } - - public static StructureTemplatePool decode(final JsonElement json, final RegistryAccess registryAccess) { - final RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess); - return StructureTemplatePool.DIRECT_CODEC.parse(ops, json).getOrThrow(); - } - - public static SpongeJigsawPoolTemplate decode(final DataPack pack, final ResourceKey key, final JsonElement packEntry, final RegistryAccess registryAccess) { - final StructureTemplatePool parsed = SpongeJigsawPoolTemplate.decode(packEntry, registryAccess); - return new SpongeJigsawPoolTemplate(key, parsed, pack); - } - - public static final class BuilderImpl extends AbstractDataPackEntryBuilder implements Builder { - - private Holder fallback; - private List> templates; - - public BuilderImpl() { - this.reset(); - } - - @Override - public Function valueExtractor() { - return JigsawPoolTemplate::jigsawPool; - } - - @Override - public Builder fromValue(final JigsawPool StructureTemplatePool) { - var mcPool = (StructureTemplatePool) StructureTemplatePool; - this.fallback = mcPool.getFallback(); - this.templates = new ArrayList<>(((StructureTemplatePoolAccessor) mcPool).accessor$rawTemplates()); - return this; - } - - @Override - public Builder fromDataPack(final DataView pack) throws IOException { - final JsonElement json = JsonParser.parseString(DataFormats.JSON.get().write(pack)); - final StructureTemplatePool decoded = SpongeJigsawPoolTemplate.decode(json, SpongeCommon.server().registryAccess()); - return this.fromValue((JigsawPool) decoded); - } - - @Override - public Builder add(final JigsawPoolElement element, final int weight) { - this.templates.add(Pair.of((StructurePoolElement) element, weight)); - return this; - } - - @Override - public Builder name(final ResourceKey name) { -// TODO this.name = (ResourceLocation) (Object) name; - return this; - } - - @Override - public Builder fallback(final RegistryReference fallback) { -// TODO this.fallback = (ResourceLocation) (Object) fallback.location(); - return this; - } - - @Override - public Builder fallback(final JigsawPoolTemplate fallback) { -// TODO this.fallback = (ResourceLocation) (Object) fallback.key(); - return this; - } - - @Override - public Builder reset() { - super.reset(); - this.pack = DataPacks.JIGSAW_POOL; - // TODO this.name = null; - // TODO this.fallback = new ResourceLocation("empty"); - this.templates = new ArrayList<>(); - return this; - } - - @Override - protected SpongeJigsawPoolTemplate build0() { - // TODO Objects.requireNonNull(this.name, "name"); - return new SpongeJigsawPoolTemplate(this.key, new StructureTemplatePool(this.fallback, this.templates), this.pack); - } - } -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureProcessorTypeMixin_API.java b/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeProcessorFactory.java similarity index 50% rename from src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureProcessorTypeMixin_API.java rename to src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeProcessorFactory.java index 6a2c6060c27..729f4bbd1ca 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/level/levelgen/structure/templatesystem/StructureProcessorTypeMixin_API.java +++ b/src/main/java/org/spongepowered/common/world/generation/structure/jigsaw/SpongeProcessorFactory.java @@ -22,37 +22,50 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.mixin.api.minecraft.world.level.levelgen.structure.templatesystem; +package org.spongepowered.common.world.generation.structure.jigsaw; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.mojang.serialization.JsonOps; import com.mojang.serialization.MapCodec; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.Identifier; import net.minecraft.resources.RegistryOps; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.data.persistence.DataFormats; import org.spongepowered.api.data.persistence.DataView; +import org.spongepowered.api.registry.RegistryHolder; import org.spongepowered.api.world.generation.structure.jigsaw.Processor; -import org.spongepowered.api.world.generation.structure.jigsaw.ProcessorType; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.registry.SpongeRegistryHolder; import java.io.IOException; +import java.util.Objects; -@Mixin(StructureProcessorType.class) -public interface StructureProcessorTypeMixin_API