diff --git a/.editorconfig b/.editorconfig index 21392b2d0..89050be96 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,4 +2,14 @@ root = true # Indentation override for all java files [**.java] +indent_style = space indent_size = 4 +insert_final_newline = true +ij_continuation_indent_size = 8 + +# Indentation override for all +[pom.xml] +indent_style = space +indent_size = 4 +insert_final_newline = false +ij_continuation_indent_size = 8 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..2472e19a6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,126 @@ +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/IF" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/adventure-support" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_14" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_15" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_16_1" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_16_2-3" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_16_4-5" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_17_0" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_17_1" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_18_0" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_18_1" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_18_2" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_19_0" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_19_1" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_19_2" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_19_3" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_19_4" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_20_0" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_20_1" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_20_2" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_20_3-4" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_20_5" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_20_6" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_21_0" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_21_1" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_21_2-3" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/abstraction" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/inventory-view/iv-abstract-class" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/inventory-view/iv-abstraction" + schedule: + interval: "daily" + - package-ecosystem: "maven" + directory: "/inventory-view/iv-interface" + schedule: + interval: "daily" diff --git a/.github/deployment/deploy.sh b/.github/deployment/deploy.sh new file mode 100644 index 000000000..ccf6f861c --- /dev/null +++ b/.github/deployment/deploy.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +PARENT_VERSION=$(mvn help:evaluate -Dexpression=project.version -pl :IF-parent -q -DforceStdout) +BASE_VERSION=$(mvn help:evaluate -Dexpression=project.version -pl :IF -q -DforceStdout) + +if [[ $PARENT_VERSION != "$BASE_VERSION" ]]; then + echo "IF-parent and IF versions mismatch" + exit 1 +fi + +if [[ $PARENT_VERSION != *-SNAPSHOT ]]; then + exit 0 +fi + +if ! mvn clean install -B; then + echo "Unable to build IF" + exit 1 +fi + +if ! mvn deploy -N -pl :IF-parent -P deploy -s ./.github/deployment/settings.xml -B -Dgpg.passphrase="$1" -Ddeploy.username="$2" -Ddeploy.password="$3"; then + echo "Unable to deploy IF-parent" + exit 1 +fi + +if ! mvn deploy -pl :IF -P deploy -s ./.github/deployment/settings.xml -B -Dgpg.passphrase="$1" -Ddeploy.username="$2" -Ddeploy.password="$3"; then + echo "Unable to deploy IF" +fi diff --git a/.github/deployment/settings.xml b/.github/deployment/settings.xml new file mode 100644 index 000000000..6f1a37206 --- /dev/null +++ b/.github/deployment/settings.xml @@ -0,0 +1,9 @@ + + + + ossrh + ${deploy.username} + ${deploy.password} + + + \ No newline at end of file diff --git a/.github/workflows/auto-deploy.yml b/.github/workflows/auto-deploy.yml new file mode 100644 index 000000000..0ec441f24 --- /dev/null +++ b/.github/workflows/auto-deploy.yml @@ -0,0 +1,137 @@ +name: Automatically deploy the project + +on: + push: + branches: + - master + +jobs: + deploy: + runs-on: ubuntu-latest + name: 'Deploy' + steps: + - name: Cache Paper(clip) jars + id: cache-paperclip-jars + uses: actions/cache@v2 + with: + path: | + .paper-nms + build + paperclip + ~/.m2/repository/io/papermc/paper + ~/.m2/repository/ca/bkaw/paper-nms + ~/.m2/repository/org/spigotmc/spigot + key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-paperclip + - name: Download Paperclip jars + if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' + run: | + mkdir -p paperclip + wget https://papermc.io/api/v2/projects/paper/versions/1.14.4/builds/243/downloads/paper-1.14.4-243.jar -O paperclip/paper-1.14.4.jar + wget https://papermc.io/api/v2/projects/paper/versions/1.15.2/builds/391/downloads/paper-1.15.2-391.jar -O paperclip/paper-1.15.2.jar + wget https://papermc.io/api/v2/projects/paper/versions/1.16.1/builds/138/downloads/paper-1.16.1-138.jar -O paperclip/paper-1.16.1.jar + wget https://papermc.io/api/v2/projects/paper/versions/1.16.3/builds/253/downloads/paper-1.16.3-253.jar -O paperclip/paper-1.16.3.jar + wget https://papermc.io/api/v2/projects/paper/versions/1.16.4/builds/416/downloads/paper-1.16.4-416.jar -O paperclip/paper-1.16.4.jar + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Generate 1.14 - 1.16 Paper jars + if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' + working-directory: paperclip + run: | + java -jar paper-1.14.4.jar + java -jar paper-1.15.2.jar + java -jar paper-1.16.1.jar + java -jar paper-1.16.3.jar + java -jar paper-1.16.4.jar + - name: Install 1.14 - 1.16 Paper jars + if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' + working-directory: paperclip + run: | + mvn install:install-file -Dfile=cache/patched_1.14.4.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.14.4-R0.1-SNAPSHOT" -Dpackaging="jar" + mvn install:install-file -Dfile=cache/patched_1.15.2.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.15.2-R0.1-SNAPSHOT" -Dpackaging="jar" + mvn install:install-file -Dfile=cache/patched_1.16.1.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.1-R0.1-SNAPSHOT" -Dpackaging="jar" + mvn install:install-file -Dfile=cache/patched_1.16.3.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.3-R0.1-SNAPSHOT" -Dpackaging="jar" + mvn install:install-file -Dfile=cache/patched_1.16.4.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.4-R0.1-SNAPSHOT" -Dpackaging="jar" + - name: Set up JDK 21 + uses: actions/setup-java@v1 + with: + java-version: 21 + - uses: actions/checkout@v2 + name: Checkout code + - name: Run paper-nms init + if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' + run: | + mvn paper-nms:init -pl nms/1_17_0 + mvn paper-nms:init -pl nms/1_17_1 + mvn paper-nms:init -pl nms/1_18_0 + mvn paper-nms:init -pl nms/1_18_1 + mvn paper-nms:init -pl nms/1_18_2 + mvn paper-nms:init -pl nms/1_19_0 + mvn paper-nms:init -pl nms/1_19_1 + mvn paper-nms:init -pl nms/1_19_2 + mvn paper-nms:init -pl nms/1_19_3 + mvn paper-nms:init -pl nms/1_19_4 + mvn paper-nms:init -pl nms/1_20_0 + mvn paper-nms:init -pl nms/1_20_1 + mvn paper-nms:init -pl nms/1_20_2 + mvn paper-nms:init -pl nms/1_20_3-4 + - name: 'Run BuildTools 1.20.5-1.20.6, 1.21' + if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' + run: | + mkdir -p build + cd build/ + wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O BuildTools.jar + + git clone https://hub.spigotmc.org/stash/scm/spigot/bukkit.git Bukkit + cd Bukkit + git checkout 304e83eb384c338546aa96eea51388e0e8407e26 + cd .. + + git clone https://hub.spigotmc.org/stash/scm/spigot/craftbukkit.git CraftBukkit + cd CraftBukkit + git checkout 91b1fc3f1cf89e2591367dca1fa7362fe376f289 + cd .. + + git clone https://hub.spigotmc.org/stash/scm/spigot/spigot.git Spigot + cd Spigot + git checkout b698b49caf14f97a717afd67e13fd7ac59f51089 + cd .. + + git clone https://hub.spigotmc.org/stash/scm/spigot/builddata.git BuildData + cd BuildData + git checkout a7f7c2118b877fde4cf0f32f1f730ffcdee8e9ee + cd .. + + java -jar BuildTools.jar --remapped --disable-java-check --dont-update + java -jar BuildTools.jar --rev 1.20.6 --remapped --disable-java-check + + cd Bukkit + git checkout 2ec53f498e32b3af989cb24672fc54dfab087154 + cd .. + + cd CraftBukkit + git checkout 8ee6fd1b8db9896590aa321d0199453de1fc35db + cd .. + + cd Spigot + git checkout fb8fb722a327a2f9f097f2ded700ac5de8157408 + cd .. + + cd BuildData + git checkout ae1e7b1e31cd3a3892bb05a6ccdcecc48c73c455 + cd .. + + java -jar BuildTools.jar --remapped --disable-java-check --dont-update + java -jar BuildTools.jar --rev 1.21.1 --remapped --disable-java-check + java -jar BuildTools.jar --rev 1.21.3 --remapped --disable-java-check + + cd ../ + - name: Setup GPG keys + run: cat <(echo -e "${{ secrets.GPG_KEY }}") | gpg --batch --import + - name: Automatically deploy the project + env: + gpg_passphrase: ${{ secrets.GPG_PASSPHRASE }} + deploy_username: ${{ secrets.DEPLOY_USERNAME }} + deploy_password: ${{ secrets.DEPLOY_PASSWORD }} + run: bash ./.github/deployment/deploy.sh "$gpg_passphrase" "$deploy_username" "$deploy_password" diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 9df069e74..ae799f8a0 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -16,17 +16,122 @@ jobs: runs-on: ubuntu-latest name: 'Build' steps: - - run: | - wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O BuildTools.jar - java -jar BuildTools.jar --rev 1.14.4 - java -jar BuildTools.jar --rev 1.15.2 - java -jar BuildTools.jar --rev 1.16.1 - java -jar BuildTools.jar --rev 1.16.3 - java -jar BuildTools.jar --rev 1.16.4 - - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Cache Paper(clip) jars + id: cache-paperclip-jars + uses: actions/cache@v2 + with: + path: | + .paper-nms + build + paperclip + ~/.m2/repository/io/papermc/paper + ~/.m2/repository/ca/bkaw/paper-nms + ~/.m2/repository/org/spigotmc/spigot + key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-paperclip + - name: Download Paperclip jars + if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' + run: | + mkdir -p paperclip + wget https://papermc.io/api/v2/projects/paper/versions/1.14.4/builds/243/downloads/paper-1.14.4-243.jar -O paperclip/paper-1.14.4.jar + wget https://papermc.io/api/v2/projects/paper/versions/1.15.2/builds/391/downloads/paper-1.15.2-391.jar -O paperclip/paper-1.15.2.jar + wget https://papermc.io/api/v2/projects/paper/versions/1.16.1/builds/138/downloads/paper-1.16.1-138.jar -O paperclip/paper-1.16.1.jar + wget https://papermc.io/api/v2/projects/paper/versions/1.16.3/builds/253/downloads/paper-1.16.3-253.jar -O paperclip/paper-1.16.3.jar + wget https://papermc.io/api/v2/projects/paper/versions/1.16.4/builds/416/downloads/paper-1.16.4-416.jar -O paperclip/paper-1.16.4.jar + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 + - name: Generate 1.14 - 1.16 Paper jars + if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' + working-directory: paperclip + run: | + java -jar paper-1.14.4.jar + java -jar paper-1.15.2.jar + java -jar paper-1.16.1.jar + java -jar paper-1.16.3.jar + java -jar paper-1.16.4.jar + - name: Install 1.14 - 1.16 Paper jars + if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' + working-directory: paperclip + run: | + mvn install:install-file -Dfile=cache/patched_1.14.4.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.14.4-R0.1-SNAPSHOT" -Dpackaging="jar" + mvn install:install-file -Dfile=cache/patched_1.15.2.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.15.2-R0.1-SNAPSHOT" -Dpackaging="jar" + mvn install:install-file -Dfile=cache/patched_1.16.1.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.1-R0.1-SNAPSHOT" -Dpackaging="jar" + mvn install:install-file -Dfile=cache/patched_1.16.3.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.3-R0.1-SNAPSHOT" -Dpackaging="jar" + mvn install:install-file -Dfile=cache/patched_1.16.4.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.4-R0.1-SNAPSHOT" -Dpackaging="jar" + - name: Set up JDK 21 + uses: actions/setup-java@v1 + with: + java-version: 21 + - uses: actions/checkout@v2 + name: Checkout code + - name: Run paper-nms init + if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' + run: | + mvn paper-nms:init -pl nms/1_17_0 + mvn paper-nms:init -pl nms/1_17_1 + mvn paper-nms:init -pl nms/1_18_0 + mvn paper-nms:init -pl nms/1_18_1 + mvn paper-nms:init -pl nms/1_18_2 + mvn paper-nms:init -pl nms/1_19_0 + mvn paper-nms:init -pl nms/1_19_1 + mvn paper-nms:init -pl nms/1_19_2 + mvn paper-nms:init -pl nms/1_19_3 + mvn paper-nms:init -pl nms/1_19_4 + mvn paper-nms:init -pl nms/1_20_0 + mvn paper-nms:init -pl nms/1_20_1 + mvn paper-nms:init -pl nms/1_20_2 + mvn paper-nms:init -pl nms/1_20_3-4 + - name: 'Run BuildTools 1.20.5-1.20.6, 1.21' + if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' + run: | + mkdir -p build + cd build/ + wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O BuildTools.jar + + git clone https://hub.spigotmc.org/stash/scm/spigot/bukkit.git Bukkit + cd Bukkit + git checkout 304e83eb384c338546aa96eea51388e0e8407e26 + cd .. + + git clone https://hub.spigotmc.org/stash/scm/spigot/craftbukkit.git CraftBukkit + cd CraftBukkit + git checkout 91b1fc3f1cf89e2591367dca1fa7362fe376f289 + cd .. + + git clone https://hub.spigotmc.org/stash/scm/spigot/spigot.git Spigot + cd Spigot + git checkout b698b49caf14f97a717afd67e13fd7ac59f51089 + cd .. + + git clone https://hub.spigotmc.org/stash/scm/spigot/builddata.git BuildData + cd BuildData + git checkout a7f7c2118b877fde4cf0f32f1f730ffcdee8e9ee + cd .. + + java -jar BuildTools.jar --remapped --disable-java-check --dont-update + java -jar BuildTools.jar --rev 1.20.6 --remapped --disable-java-check + + cd Bukkit + git checkout 2ec53f498e32b3af989cb24672fc54dfab087154 + cd .. + + cd CraftBukkit + git checkout 8ee6fd1b8db9896590aa321d0199453de1fc35db + cd .. + + cd Spigot + git checkout fb8fb722a327a2f9f097f2ded700ac5de8157408 + cd .. + + cd BuildData + git checkout ae1e7b1e31cd3a3892bb05a6ccdcecc48c73c455 + cd .. + + java -jar BuildTools.jar --remapped --disable-java-check --dont-update + java -jar BuildTools.jar --rev 1.21.1 --remapped --disable-java-check + java -jar BuildTools.jar --rev 1.21.3 --remapped --disable-java-check + + cd ../ - name: Build with Maven run: mvn -B package --file pom.xml diff --git a/.gitignore b/.gitignore index 77a4b1128..c78733736 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ target IF-Test *.iml *.txt -dependency-reduced-pom.xml \ No newline at end of file +dependency-reduced-pom.xml +*.log +.paper-nms \ No newline at end of file diff --git a/IF/pom.xml b/IF/pom.xml index 90d71667b..e14f55919 100644 --- a/IF/pom.xml +++ b/IF/pom.xml @@ -5,7 +5,8 @@ IF-parent com.github.stefvanschie.inventoryframework - 0.9.8 + 0.10.18 + ../pom.xml 4.0.0 @@ -28,6 +29,12 @@ + + net.kyori + adventure-api + ${adventure.version} + provided + com.github.stefvanschie.inventoryframework abstraction @@ -36,50 +43,196 @@ com.github.stefvanschie.inventoryframework - 1_14_R1 + 1_14 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_15 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_16_1 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_16_2-3 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_16_4-5 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_17_0 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_17_1 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_18_0 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_18_1 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_18_2 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_19_0 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_19_1 ${project.version} compile com.github.stefvanschie.inventoryframework - 1_15_R1 + 1_19_2 ${project.version} compile com.github.stefvanschie.inventoryframework - 1_16_R1 + 1_19_3 ${project.version} compile com.github.stefvanschie.inventoryframework - 1_16_R2 + 1_19_4 ${project.version} compile com.github.stefvanschie.inventoryframework - 1_16_R3 + 1_20_0 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_20_1 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_20_2 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_20_3-4 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_20_5 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_20_6 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_21_0 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_21_1 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + 1_21_2-3 + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + iv-abstraction + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + iv-abstract-class + ${project.version} + compile + + + com.github.stefvanschie.inventoryframework + iv-interface ${project.version} compile org.spigotmc spigot-api - 1.16.4-R0.1-SNAPSHOT + 1.20.3-R0.1-SNAPSHOT provided + + + + org.apache.commons + commons-lang3 + + com.mojang authlib - 1.5.21 + 1.5.26 provided + + + + org.apache.commons + commons-lang3 + + org.junit.jupiter - junit-jupiter-api - 5.3.1 + junit-jupiter-engine + 5.11.0 test @@ -103,7 +256,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.2.5 sign-artifacts @@ -126,24 +279,19 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.0 + 3.5.0 - org.junit.platform - junit-platform-surefire-provider - 1.2.0-M1 - - - org.junit.jupiter - junit-jupiter-engine - 5.2.0 + org.apache.maven.surefire + surefire-junit-platform + 3.5.2 org.apache.maven.plugins maven-shade-plugin - 3.2.1 + 3.6.0 package @@ -156,7 +304,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.7.0 true ossrh @@ -167,7 +315,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.0 + 3.3.1 attach-sources @@ -180,7 +328,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.1.1 + 3.10.0 attach-javadocs diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/HumanEntityCache.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/HumanEntityCache.java index 51d6920c7..cb0b11798 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/HumanEntityCache.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/HumanEntityCache.java @@ -155,6 +155,17 @@ private void restoreAll() { inventories.keySet().forEach(this::restore); } + /** + * Checks if the provided human entity appears in the cache, returning true if this is the case. + * + * @param humanEntity the human entity to check whether it is present in the cache + * @return true if the human entity is in the cache, false otherwise + * @since 0.10.7 + */ + public boolean contains(@NotNull HumanEntity humanEntity) { + return this.inventories.containsKey(humanEntity); + } + /** * Clear the cache for the specified human entity * diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/GuiItem.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/GuiItem.java index fe09b0d6a..3c63c1203 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/GuiItem.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/GuiItem.java @@ -1,5 +1,6 @@ package com.github.stefvanschie.inventoryframework.gui; +import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil; import com.github.stefvanschie.inventoryframework.util.UUIDTagType; import org.bukkit.NamespacedKey; import org.bukkit.event.inventory.InventoryClickEvent; @@ -7,6 +8,7 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -24,11 +26,18 @@ */ public class GuiItem { + /** + * The logger to log errors with + */ + @NotNull + private final Logger logger; + /** * The {@link NamespacedKey} that specifies the location of the (internal) {@link UUID} in {@link PersistentDataContainer}s. * The {@link PersistentDataType} that should be used is {@link UUIDTagType}. */ - public static final NamespacedKey KEY_UUID = new NamespacedKey(JavaPlugin.getProvidingPlugin(GuiItem.class), "IF-uuid"); + @NotNull + private final NamespacedKey keyUUID; /** * An action for the inventory @@ -46,7 +55,7 @@ public class GuiItem { * The items shown */ @NotNull - private final ItemStack item; + private ItemStack item; /** * Whether this item is visible or not @@ -64,15 +73,34 @@ public class GuiItem { * * @param item the item stack * @param action the action called whenever an interaction with this item happens + * @param plugin the owning plugin of this item + * @see #GuiItem(ItemStack, Consumer) + * @since 0.10.8 */ - public GuiItem(@NotNull ItemStack item, @Nullable Consumer action) { - this.action = action; - this.visible = true; - this.properties = new ArrayList<>(); - this.item = item; + public GuiItem(@NotNull ItemStack item, @Nullable Consumer action, @NotNull Plugin plugin) { + this(item, action, plugin.getLogger(), new NamespacedKey(plugin, "IF-uuid")); + } - //remove this call after the removal of InventoryComponent#setItem(ItemStack, int, int) - applyUUID(); + /** + * Creates a new gui item based on the item stack and action + * + * @param item the item stack + * @param plugin the owning plugin of this item + * @see #GuiItem(ItemStack) + * @since 0.10.8 + */ + public GuiItem(@NotNull ItemStack item, @NotNull Plugin plugin) { + this(item, event -> {}, plugin); + } + + /** + * Creates a new gui item based on the item stack and action + * + * @param item the item stack + * @param action the action called whenever an interaction with this item happens + */ + public GuiItem(@NotNull ItemStack item, @Nullable Consumer action) { + this(item, action, JavaPlugin.getProvidingPlugin(GuiItem.class)); } /** @@ -81,7 +109,30 @@ public GuiItem(@NotNull ItemStack item, @Nullable Consumer * @param item the item stack */ public GuiItem(@NotNull ItemStack item) { - this(item, null); + this(item, event -> {}); + } + + /** + * Creates a new gui item based on the given item, action, logger, and key. The logger will be used for logging + * exceptions and the key is used for identification of this item. + * + * @param item the item stack + * @param action the action called whenever an interaction with this item happens + * @param logger the logger used for logging exceptions + * @param key the key to identify this item with + * @since 0.10.10 + */ + private GuiItem(@NotNull ItemStack item, @Nullable Consumer action, @NotNull Logger logger, + @NotNull NamespacedKey key) { + this.logger = logger; + this.keyUUID = key; + this.action = action; + this.visible = true; + this.properties = new ArrayList<>(); + this.item = item; + + //remove this call after the removal of InventoryComponent#setItem(ItemStack, int, int) + applyUUID(); } /** @@ -95,20 +146,18 @@ public GuiItem(@NotNull ItemStack item) { @NotNull @Contract(pure = true) public GuiItem copy() { - GuiItem guiItem = new GuiItem(item.clone(), action); + GuiItem guiItem = new GuiItem(item.clone(), action, this.logger, this.keyUUID); guiItem.visible = visible; guiItem.uuid = uuid; guiItem.properties = new ArrayList<>(properties); ItemMeta meta = guiItem.item.getItemMeta(); - if (meta == null) { - throw new IllegalArgumentException("item must be able to have ItemMeta (it mustn't be AIR)"); + if (meta != null) { + meta.getPersistentDataContainer().set(keyUUID, UUIDTagType.INSTANCE, guiItem.uuid); + guiItem.item.setItemMeta(meta); } - meta.getPersistentDataContainer().set(KEY_UUID, UUIDTagType.INSTANCE, guiItem.uuid); - guiItem.item.setItemMeta(meta); - return guiItem; } @@ -128,9 +177,9 @@ public void callAction(@NotNull InventoryClickEvent event) { try { action.accept(event); } catch (Throwable t) { - Logger logger = JavaPlugin.getProvidingPlugin(getClass()).getLogger(); - logger.log(Level.SEVERE, "Exception while handling click event in inventory '" - + event.getView().getTitle() + "', slot=" + event.getSlot() + ", item=" + item.getType(), t); + this.logger.log(Level.SEVERE, "Exception while handling click event in inventory '" + + InventoryViewUtil.getInstance().getTitle(event.getView()) + "', slot=" + event.getSlot() + + ", item=" + item.getType(), t); } } @@ -144,11 +193,21 @@ public void applyUUID() { ItemMeta meta = item.getItemMeta(); if (meta != null) { - meta.getPersistentDataContainer().set(KEY_UUID, UUIDTagType.INSTANCE, uuid); + meta.getPersistentDataContainer().set(this.keyUUID, UUIDTagType.INSTANCE, uuid); item.setItemMeta(meta); } } + /** + * Overwrites the current item with the provided item. + * + * @param item the item to set + * @since 0.10.8 + */ + public void setItem(@NotNull ItemStack item) { + this.item = item; + } + /** * Sets the action to be executed when a human entity clicks on this item. * @@ -192,6 +251,18 @@ public ItemStack getItem() { return item; } + /** + * Gets the namespaced key used for this item. + * + * @return the namespaced key + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + public NamespacedKey getKey() { + return keyUUID; + } + /** * Gets the {@link UUID} associated with this {@link GuiItem}. This is for internal use only, and should not be * used. diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/GuiListener.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/GuiListener.java index 0703a5e0b..ea1008114 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/GuiListener.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/GuiListener.java @@ -2,8 +2,10 @@ import com.github.stefvanschie.inventoryframework.gui.type.*; import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; +import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil; import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -13,12 +15,12 @@ import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.inventory.*; import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.logging.Logger; /** * Listens to events for {@link Gui}s. Only one instance of this class gets constructed. @@ -28,12 +30,28 @@ */ public class GuiListener implements Listener { + /** + * The owning plugin of this listener. + */ + @NotNull + private final Plugin plugin; + /** * A collection of all {@link Gui} instances that have at least one viewer. */ @NotNull private final Set activeGuiInstances = new HashSet<>(); + /** + * Creates a new listener for all guis for the provided {@code plugin}. + * + * @param plugin the owning plugin of this listener + * @since 0.10.8 + */ + public GuiListener(@NotNull Plugin plugin) { + this.plugin = plugin; + } + /** * Handles clicks in inventories * @@ -49,7 +67,7 @@ public void onInventoryClick(@NotNull InventoryClickEvent event) { } InventoryView view = event.getView(); - Inventory inventory = view.getInventory(event.getRawSlot()); + Inventory inventory = InventoryViewUtil.getInstance().getInventory(view, event.getRawSlot()); if (inventory == null) { gui.callOnOutsideClick(event); @@ -57,13 +75,23 @@ public void onInventoryClick(@NotNull InventoryClickEvent event) { } gui.callOnGlobalClick(event); - if (inventory.equals(view.getTopInventory())) { + if (inventory.equals(InventoryViewUtil.getInstance().getTopInventory(view))) { gui.callOnTopClick(event); } else { gui.callOnBottomClick(event); } gui.click(event); + + if (event.isCancelled()) { + Bukkit.getScheduler().runTask(this.plugin, () -> { + PlayerInventory playerInventory = event.getWhoClicked().getInventory(); + + /* due to a client issue off-hand items appear as ghost items, this updates the off-hand correctly + client-side */ + playerInventory.setItemInOffHand(playerInventory.getItemInOffHand()); + }); + } } /** @@ -71,8 +99,9 @@ public void onInventoryClick(@NotNull InventoryClickEvent event) { * * @param event the event fired * @since 0.8.0 + * @deprecated no longer used internally */ - @EventHandler(priority = EventPriority.HIGHEST) + @Deprecated public void resetItemsAnvil(@NotNull InventoryClickEvent event) { InventoryHolder holder = event.getInventory().getHolder(); @@ -139,8 +168,9 @@ public void resetItemsEnchantingTable(@NotNull InventoryClickEvent event) { * * @param event the event fired * @since 0.8.0 + * @deprecated no longer used internally */ - @EventHandler(priority = EventPriority.HIGHEST) + @Deprecated public void resetItemsGrindstone(@NotNull InventoryClickEvent event) { InventoryHolder holder = event.getInventory().getHolder(); @@ -193,17 +223,19 @@ public void resetItemsSmithingTable(@NotNull InventoryClickEvent event) { */ @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onEntityPickupItem(@NotNull EntityPickupItemEvent event) { - if (!(event.getEntity() instanceof HumanEntity)) { + LivingEntity entity = event.getEntity(); + + if (!(entity instanceof HumanEntity)) { return; } - Gui gui = getGui(((HumanEntity) event.getEntity()).getOpenInventory().getTopInventory()); + Gui gui = getGui(InventoryViewUtil.getInstance().getTopInventory(((HumanEntity) entity).getOpenInventory())); if (gui == null || !gui.isPlayerInventoryUsed()) { return; } - int leftOver = gui.getHumanEntityCache().add((HumanEntity) event.getEntity(), event.getItem().getItemStack()); + int leftOver = gui.getHumanEntityCache().add((HumanEntity) entity, event.getItem().getItemStack()); if (leftOver == 0) { event.getItem().remove(); @@ -233,17 +265,17 @@ public void onInventoryDrag(@NotNull InventoryDragEvent event) { } InventoryView view = event.getView(); - Set inventorySlots = event.getInventorySlots(); + Set inventorySlots = event.getRawSlots(); if (inventorySlots.size() > 1) { boolean top = false, bottom = false; - for (int inventorySlot : event.getRawSlots()) { - Inventory inventory = view.getInventory(inventorySlot); + for (int inventorySlot : inventorySlots) { + Inventory inventory = InventoryViewUtil.getInstance().getInventory(view, inventorySlot); - if (view.getTopInventory().equals(inventory)) { + if (InventoryViewUtil.getInstance().getTopInventory(view).equals(inventory)) { top = true; - } else if (view.getBottomInventory().equals(inventory)) { + } else if (InventoryViewUtil.getInstance().getBottomInventory(view).equals(inventory)) { bottom = true; } @@ -263,23 +295,46 @@ public void onInventoryDrag(@NotNull InventoryDragEvent event) { } } else { int index = inventorySlots.toArray(new Integer[0])[0]; - InventoryType.SlotType slotType = view.getSlotType(index); + InventoryType.SlotType slotType = InventoryViewUtil.getInstance().getSlotType(view, index); boolean even = event.getType() == DragType.EVEN; ClickType clickType = even ? ClickType.LEFT : ClickType.RIGHT; InventoryAction inventoryAction = even ? InventoryAction.PLACE_SOME : InventoryAction.PLACE_ONE; + ItemStack previousViewCursor = InventoryViewUtil.getInstance().getCursor(view); + // Overwrite getCursor in inventory click event to mimic real event fired by Bukkit. + InventoryViewUtil.getInstance().setCursor(view, event.getOldCursor()); //this is a fake click event, firing this may cause other plugins to function incorrectly, so keep it local InventoryClickEvent inventoryClickEvent = new InventoryClickEvent(view, slotType, index, clickType, inventoryAction); onInventoryClick(inventoryClickEvent); + // Restore previous cursor only if someone has not changed it manually in onInventoryClick. + if (Objects.equals(InventoryViewUtil.getInstance().getCursor(view), event.getOldCursor())) { + InventoryViewUtil.getInstance().setCursor(view, previousViewCursor); + } event.setCancelled(inventoryClickEvent.isCancelled()); } } + /** + * Handles the selection of trades in merchant guis + * + * @param event the event fired + */ + @EventHandler(ignoreCancelled = true) + public void onTradeSelect(@NotNull TradeSelectEvent event) { + Gui gui = getGui(event.getInventory()); + + if (!(gui instanceof MerchantGui)) { + return; + } + + ((MerchantGui) gui).callOnTradeSelect(event); + } + /** * Handles closing in inventories * @@ -300,21 +355,27 @@ public void onInventoryClose(@NotNull InventoryCloseEvent event) { //due to a client issue off-hand items appear as ghost items, this updates the off-hand correctly client-side playerInventory.setItemInOffHand(playerInventory.getItemInOffHand()); - if (gui.isUpdating()) { + if (!gui.isUpdating()) { + gui.callOnClose(event); + + event.getInventory().clear(); //clear inventory to prevent items being put back + gui.getHumanEntityCache().restoreAndForget(humanEntity); if (gui.getViewerCount() == 1) { activeGuiInstances.remove(gui); } - } else { - gui.callOnClose(event); - //this is a hack to remove items correctly when players press the x button in a beacon - Bukkit.getScheduler().runTask(JavaPlugin.getProvidingPlugin(getClass()), () -> { - if (humanEntity.getOpenInventory().getTopInventory() instanceof PlayerInventory) { - humanEntity.closeInventory(); - } - }); + if (gui instanceof AnvilGui) { + ((AnvilGui) gui).handleClose(humanEntity); + } else if (gui instanceof MerchantGui) { + ((MerchantGui) gui).handleClose(humanEntity); + } else if (gui instanceof ModernSmithingTableGui) { + ((ModernSmithingTableGui) gui).handleClose(humanEntity); + } + + //Bukkit doesn't like it if you open an inventory while the previous one is being closed + Bukkit.getScheduler().runTask(this.plugin, () -> gui.navigateToParent(humanEntity)); } } @@ -343,8 +404,7 @@ public void onInventoryOpen(@NotNull InventoryOpenEvent event) { */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPluginDisable(@NotNull PluginDisableEvent event) { - Plugin thisPlugin = JavaPlugin.getProvidingPlugin(getClass()); - if (event.getPlugin() != thisPlugin) { + if (event.getPlugin() != this.plugin) { return; } @@ -359,8 +419,11 @@ public void onPluginDisable(@NotNull PluginDisableEvent event) { } if (counter == maxCount) { - thisPlugin.getLogger().warning("Unable to close GUIs on plugin disable: they keep getting opened " - + "(tried: " + maxCount + " times)"); + Logger logger = this.plugin.getLogger(); + + logger.warning( + "Unable to close GUIs on plugin disable: they keep getting opened (tried: " + maxCount + " times)" + ); } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/InventoryComponent.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/InventoryComponent.java index 9dd4d6d12..fa23496f5 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/InventoryComponent.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/InventoryComponent.java @@ -1,11 +1,14 @@ package com.github.stefvanschie.inventoryframework.gui; import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.pane.Pane; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -122,10 +125,10 @@ public void display(@NotNull Inventory inventory, int offset) { * This will make each pane in this component render their items in this inventory component. The panes are * displayed according to their priority, with the lowest priority rendering first and the highest priority (note: * highest priority, not {@link Pane.Priority#HIGHEST} priority) rendering last. The items displayed in this - * inventory component will be put into the inventory found in {@link Gui#getInventory()}. The slots will be placed - * from the top-right to the bottom-left, continuing from left-to-right, top-to-bottom plus the specified offset. - * This ordering is different from the normal ordering of the indices of a {@link PlayerInventory}. See for the - * normal ordering of a {@link PlayerInventory}'s slots its documentation. + * inventory component will be put into the inventory found in {@link InventoryBased#getInventory()}. The slots will + * be placed from the top-right to the bottom-left, continuing from left-to-right, top-to-bottom plus the specified + * offset. This ordering is different from the normal ordering of the indices of a {@link PlayerInventory}. See for + * the normal ordering of a {@link PlayerInventory}'s slots its documentation. * * @param inventory the inventory to place the items in * @param offset the offset from which to start counting the slots @@ -195,7 +198,7 @@ public void placeItems(@NotNull Inventory inventory, int offset) { * @since 0.8.0 */ public void click(@NotNull Gui gui, @NotNull InventoryClickEvent event, int slot) { - List panes = getPanes(); + List panes = new ArrayList<>(getPanes()); //loop panes in reverse, because the highest priority pane (last in list) is most likely to have the right item for (int i = panes.size() - 1; i >= 0; i--) { @@ -297,8 +300,23 @@ public InventoryComponent excludeRows(int from, int end) { * @param instance the instance to apply field and method references on * @param element the element to load * @since 0.8.0 + * @deprecated superseded by {@link #load(Object, Element, Plugin)} */ + @Deprecated public void load(@NotNull Object instance, @NotNull Element element) { + load(instance, element, JavaPlugin.getProvidingPlugin(InventoryComponent.class)); + } + + /** + * Loads the provided element's child panes onto this component. If the element contains any child panes, this will + * mutate this component. + * + * @param instance the instance to apply field and method references on + * @param element the element to load + * @param plugin the plugin to load the panes with + * @since 0.10.12 + */ + public void load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { NodeList childNodes = element.getChildNodes(); for (int innerIndex = 0; innerIndex < childNodes.getLength(); innerIndex++) { @@ -308,7 +326,7 @@ public void load(@NotNull Object instance, @NotNull Element element) { continue; } - addPane(Gui.loadPane(instance, innerItem)); + addPane(Gui.loadPane(instance, innerItem, plugin)); } } @@ -417,9 +435,10 @@ public void setItem(@NotNull GuiItem guiItem, int x, int y) { "; should be below " + getLength() + " and " + getHeight()); } - guiItem.applyUUID(); + GuiItem copy = guiItem.copy(); + copy.applyUUID(); - this.items[x][y] = guiItem.getItem(); + this.items[x][y] = copy.getItem(); } /** diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/AnvilGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/AnvilGui.java index 2bec9e2d6..7a8e875dc 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/AnvilGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/AnvilGui.java @@ -1,18 +1,22 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; import com.github.stefvanschie.inventoryframework.util.version.Version; import com.github.stefvanschie.inventoryframework.util.version.VersionMatcher; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,13 +30,25 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.function.Consumer; +import java.util.logging.Level; /** * Represents a gui in the form of an anvil * * @since 0.8.0 */ -public class AnvilGui extends NamedGui { +public class AnvilGui extends NamedGui implements InventoryBased { + + /** + * Called whenever the name input is changed. + */ + @NotNull + private Consumer onNameInputChanged = (name) -> {}; /** * Represents the inventory component for the first item @@ -65,6 +81,12 @@ public class AnvilGui extends NamedGui { private final AnvilInventory anvilInventory = VersionMatcher.newAnvilInventory(Version.getVersion(), this); + /** + * The viewers of this gui + */ + @NotNull + private final Collection viewers = new HashSet<>(); + /** * Constructs a new anvil gui * @@ -73,6 +95,48 @@ public class AnvilGui extends NamedGui { */ public AnvilGui(@NotNull String title) { super(title); + + this.anvilInventory.subscribeToNameInputChanges(this::callOnRename); + } + + /** + * Constructs a new anvil gui + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public AnvilGui(@NotNull TextHolder title) { + super(title); + + this.anvilInventory.subscribeToNameInputChanges(this::callOnRename); + } + + /** + * Constructs a new anvil gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #AnvilGui(String) + * @since 0.10.8 + */ + public AnvilGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + + this.anvilInventory.subscribeToNameInputChanges(this::callOnRename); + } + + /** + * Constructs a new anvil gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #AnvilGui(TextHolder) + * @since 0.10.8 + */ + public AnvilGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + + this.anvilInventory.subscribeToNameInputChanges(this::callOnRename); } @Override @@ -81,9 +145,12 @@ public void show(@NotNull HumanEntity humanEntity) { throw new IllegalArgumentException("Anvils can only be opened by players"); } - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getFirstItemComponent().display(getInventory(), 0); getSecondItemComponent().display(getInventory(), 1); @@ -92,21 +159,27 @@ public void show(@NotNull HumanEntity humanEntity) { getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } - anvilInventory.openInventory((Player) humanEntity, getTitle(), getTopItems()); + Inventory inventory = anvilInventory.openInventory((Player) humanEntity, getTitleHolder(), getTopItems()); + + addInventory(inventory, this); + + this.viewers.add(humanEntity); } @NotNull @Contract(pure = true) @Override public AnvilGui copy() { - AnvilGui gui = new AnvilGui(getTitle()); + AnvilGui gui = new AnvilGui(getTitleHolder(), super.plugin); gui.firstItemComponent = firstItemComponent.copy(); gui.secondItemComponent = secondItemComponent.copy(); @@ -137,11 +210,38 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + + /** + * Sets the enchantment level cost for this anvil gui. Taking the item from the result slot will not actually remove + * these levels. Having a cost specified does not impede a player's ability to take the item in the result item, + * even if the player does not have the specified amount of levels. The cost must be a non-negative number. + * + * @param cost the cost + * @since 0.10.8 + * @throws IllegalArgumentException when the cost is less than zero + */ + public void setCost(short cost) { + if (cost < 0){ + throw new IllegalArgumentException("Cost must be non-negative"); + } + + this.anvilInventory.setCost(cost); + } + @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.ANVIL, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.ANVIL); } /** @@ -163,12 +263,27 @@ public boolean isPlayerInventoryUsed() { return getPlayerInventoryComponent().hasItem(); } + @Contract(pure = true) + @Override + public int getViewerCount() { + return this.viewers.size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(this.viewers); + } + /** * Handles an incoming inventory click event * * @param event the event to handle * @since 0.8.0 + * @deprecated no longer used internally */ + @Deprecated public void handleClickEvent(@NotNull InventoryClickEvent event) { int slot = event.getRawSlot(); Player player = (Player) event.getWhoClicked(); @@ -198,6 +313,45 @@ public void handleClickEvent(@NotNull InventoryClickEvent event) { } } + /** + * Handles a human entity closing this gui. + * + * @param humanEntity the human entity closing the gui + * @since 0.10.1 + */ + public void handleClose(@NotNull HumanEntity humanEntity) { + this.viewers.remove(humanEntity); + } + + /** + * Sets the consumer that should be called whenever the name input is changed. The argument is the new input. When + * this consumer is invoked, the value as returned by {@link #getRenameText()} will not have updated yet, hence + * allowing you to see the old value via that. + * + * @param onNameInputChanged the consumer to call when the rename input is changed + * @since 0.10.10 + */ + public void setOnNameInputChanged(@NotNull Consumer onNameInputChanged) { + this.onNameInputChanged = onNameInputChanged; + } + + /** + * Calls the consumer that was specified using {@link #setOnNameInputChanged(Consumer)}, so the consumer that should + * be called whenever the rename input is changed. Catches and logs all exceptions the consumer might throw. + * + * @param newInput the new rename input + * @since 0.10.10 + */ + private void callOnRename(@NotNull String newInput) { + try { + this.onNameInputChanged.accept(newInput); + } catch (Throwable throwable) { + String message = "Exception while handling onRename, newInput='" + newInput + "'"; + + this.plugin.getLogger().log(Level.SEVERE, message, throwable); + } + } + /** * Gets the inventory component representing the first item * @@ -267,19 +421,21 @@ private ItemStack[] getTopItems() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded anvil gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static AnvilGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static AnvilGui load(@NotNull Object instance, @NotNull InputStream inputStream, @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -291,16 +447,18 @@ public static AnvilGui load(@NotNull Object instance, @NotNull InputStream input * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will own the created gui * @return the loaded anvil gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static AnvilGui load(@NotNull Object instance, @NotNull Element element) { + public static AnvilGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - AnvilGui anvilGui = new AnvilGui(element.getAttribute("title")); + AnvilGui anvilGui = new AnvilGui(element.getAttribute("title"), plugin); anvilGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -345,9 +503,36 @@ public static AnvilGui load(@NotNull Object instance, @NotNull Element element) throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return anvilGui; } + + /** + * Loads an anvil gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded anvil gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static AnvilGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(AnvilGui.class)); + } + + /** + * Loads an anvil gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded anvil gui + * @since 0.8.0 + */ + @NotNull + public static AnvilGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(AnvilGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BarrelGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BarrelGui.java index 10815fedb..894f189d9 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BarrelGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BarrelGui.java @@ -1,16 +1,20 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.MergedGui; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; import com.github.stefvanschie.inventoryframework.pane.Pane; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -24,6 +28,7 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -33,7 +38,7 @@ * * @since 0.8.0 */ -public class BarrelGui extends NamedGui implements MergedGui { +public class BarrelGui extends NamedGui implements MergedGui, InventoryBased { /** * Represents the inventory component for the entire gui @@ -51,11 +56,48 @@ public BarrelGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public BarrelGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new barrel gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #BarrelGui(String) + * @since 0.10.8 + */ + public BarrelGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new barrel gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #BarrelGui(TextHolder) + * @since 0.10.8 + */ + public BarrelGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); int height = getInventoryComponent().getHeight(); @@ -65,14 +107,15 @@ public void show(@NotNull HumanEntity humanEntity) { InventoryComponent bottomComponent = getInventoryComponent().excludeRows(0, height - 5); topComponent.placeItems(getInventory(), 0); - bottomComponent.placeItems(humanEntity.getInventory(), 0); if (bottomComponent.hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } bottomComponent.placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -82,7 +125,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public BarrelGui copy() { - BarrelGui gui = new BarrelGui(getTitle()); + BarrelGui gui = new BarrelGui(getTitleHolder(), super.plugin); gui.inventoryComponent = inventoryComponent.copy(); @@ -95,6 +138,16 @@ public BarrelGui copy() { return gui; } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Override public void click(@NotNull InventoryClickEvent event) { getInventoryComponent().click(this, event, event.getRawSlot()); @@ -128,8 +181,21 @@ public Collection getItems() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.BARREL, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.BARREL); + } + + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); } @NotNull @@ -144,19 +210,21 @@ public InventoryComponent getInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded barrel gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static BarrelGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static BarrelGui load(@NotNull Object instance, @NotNull InputStream inputStream, @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -168,16 +236,18 @@ public static BarrelGui load(@NotNull Object instance, @NotNull InputStream inpu * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded barrel gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static BarrelGui load(@NotNull Object instance, @NotNull Element element) { + public static BarrelGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - BarrelGui barrelGui = new BarrelGui(element.getAttribute("title")); + BarrelGui barrelGui = new BarrelGui(element.getAttribute("title"), plugin); barrelGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -197,9 +267,9 @@ public static BarrelGui load(@NotNull Object instance, @NotNull Element element) InventoryComponent inventoryComponent = barrelGui.getInventoryComponent(); if (componentElement.getTagName().equalsIgnoreCase("component")) { - inventoryComponent.load(instance, componentElement); + inventoryComponent.load(instance, componentElement, plugin); } else { - inventoryComponent.load(instance, element); + inventoryComponent.load(instance, element, plugin); } break; @@ -207,4 +277,31 @@ public static BarrelGui load(@NotNull Object instance, @NotNull Element element) return barrelGui; } + + /** + * Loads a barrel gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded barrel gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static BarrelGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(BarrelGui.class)); + } + + /** + * Loads a barrel gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded barrel gui + * @since 0.8.0 + */ + @NotNull + public static BarrelGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(BarrelGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BeaconGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BeaconGui.java index d88ae7419..72ed653ae 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BeaconGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BeaconGui.java @@ -1,9 +1,11 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.util.version.Version; import com.github.stefvanschie.inventoryframework.util.version.VersionMatcher; import org.bukkit.Bukkit; @@ -12,6 +14,8 @@ import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,13 +29,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a beacon * * @since 0.8.0 */ -public class BeaconGui extends Gui { +public class BeaconGui extends Gui implements InventoryBased { /** * Represents the payment item inventory component @@ -52,6 +58,26 @@ public class BeaconGui extends Gui { private final BeaconInventory beaconInventory = VersionMatcher.newBeaconInventory(Version.getVersion(), this); + /** + * Constructs a new beacon gui. + * + * @since 0.8.0 + */ + public BeaconGui() { + this(JavaPlugin.getProvidingPlugin(BeaconGui.class)); + } + + /** + * Constructs a new beacon gui for the given {@code plugin}. + * + * @param plugin the owning plugin of this gui + * @see #BeaconGui() + * @since 0.10.8 + */ + public BeaconGui(@NotNull Plugin plugin) { + super(plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { if (!(humanEntity instanceof Player)) { @@ -60,19 +86,22 @@ public void show(@NotNull HumanEntity humanEntity) { getInventory().clear(); - getHumanEntityCache().storeAndClear(humanEntity); - getPaymentItemComponent().display(getInventory(), 0); getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); - getPlayerInventoryComponent().display(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } + + getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); } + //also let Bukkit know that we opened an inventory + humanEntity.openInventory(getInventory()); + beaconInventory.openInventory((Player) humanEntity, getPaymentItemComponent().getItem(0, 0)); } @@ -80,7 +109,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public BeaconGui copy() { - BeaconGui gui = new BeaconGui(); + BeaconGui gui = new BeaconGui(super.plugin); gui.paymentItemComponent = paymentItemComponent.copy(); gui.playerInventoryComponent = playerInventoryComponent.copy(); @@ -105,6 +134,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -118,6 +157,19 @@ public Inventory createInventory() { return Bukkit.createInventory(this, InventoryType.BEACON); } + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + /** * Handles an incoming inventory click event * @@ -166,19 +218,21 @@ public InventoryComponent getPlayerInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded beacon gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static BeaconGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static BeaconGui load(@NotNull Object instance, @NotNull InputStream inputStream, @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -190,16 +244,18 @@ public static BeaconGui load(@NotNull Object instance, @NotNull InputStream inpu * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded beacon gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static BeaconGui load(@NotNull Object instance, @NotNull Element element) { + public static BeaconGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - BeaconGui beaconGui = new BeaconGui(); + BeaconGui beaconGui = new BeaconGui(plugin); beaconGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -238,9 +294,36 @@ public static BeaconGui load(@NotNull Object instance, @NotNull Element element) throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return beaconGui; } + + /** + * Loads a beacon gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded beacon gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static BeaconGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(BeaconGui.class)); + } + + /** + * Loads a beacon gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded beacon gui + * @since 0.8.0 + */ + @NotNull + public static BeaconGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(BeaconGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BlastFurnaceGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BlastFurnaceGui.java index dc24e3e0d..a9f274b60 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BlastFurnaceGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BlastFurnaceGui.java @@ -1,13 +1,17 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,13 +25,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a blast furnace * * @since 0.8.0 */ -public class BlastFurnaceGui extends NamedGui { +public class BlastFurnaceGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the ingredient @@ -63,11 +69,48 @@ public BlastFurnaceGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public BlastFurnaceGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new blast furnace gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #BlastFurnaceGui(String) + * @since 0.10.8 + */ + public BlastFurnaceGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new blast furnace gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #BlastFurnaceGui(TextHolder) + * @since 0.10.8 + */ + public BlastFurnaceGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getIngredientComponent().display(getInventory(), 0); getFuelComponent().display(getInventory(), 1); @@ -75,11 +118,13 @@ public void show(@NotNull HumanEntity humanEntity) { getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -89,7 +134,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public BlastFurnaceGui copy() { - BlastFurnaceGui gui = new BlastFurnaceGui(getTitle()); + BlastFurnaceGui gui = new BlastFurnaceGui(getTitleHolder(), super.plugin); gui.ingredientComponent = ingredientComponent.copy(); gui.fuelComponent = fuelComponent.copy(); @@ -120,6 +165,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -129,8 +184,21 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.BLAST_FURNACE, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.BLAST_FURNACE); + } + + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); } /** @@ -186,19 +254,22 @@ public InventoryComponent getPlayerInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded blast furnace gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static BlastFurnaceGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static BlastFurnaceGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -210,16 +281,18 @@ public static BlastFurnaceGui load(@NotNull Object instance, @NotNull InputStrea * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded blast furnace gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static BlastFurnaceGui load(@NotNull Object instance, @NotNull Element element) { + public static BlastFurnaceGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - BlastFurnaceGui blastFurnaceGui = new BlastFurnaceGui(element.getAttribute("title")); + BlastFurnaceGui blastFurnaceGui = new BlastFurnaceGui(element.getAttribute("title"), plugin); blastFurnaceGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -264,9 +337,36 @@ public static BlastFurnaceGui load(@NotNull Object instance, @NotNull Element el throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return blastFurnaceGui; } + + /** + * Loads a blast furnace gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded blast furnace gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static BlastFurnaceGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(BlastFurnaceGui.class)); + } + + /** + * Loads a blast furnace gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded blast furnace gui + * @since 0.8.0 + */ + @NotNull + public static BlastFurnaceGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(BlastFurnaceGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BrewingStandGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BrewingStandGui.java index 2d9960f26..2bd166e5c 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BrewingStandGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/BrewingStandGui.java @@ -1,13 +1,17 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,13 +25,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a brewing stand * * @since 0.8.0 */ -public class BrewingStandGui extends NamedGui { +public class BrewingStandGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the first bottle @@ -75,11 +81,48 @@ public BrewingStandGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public BrewingStandGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new brewing stand gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #BrewingStandGui(String) + * @since 0.10.8 + */ + public BrewingStandGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new brewing stand gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #BrewingStandGui(TextHolder) + * @since 0.10.8 + */ + public BrewingStandGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getFirstBottleComponent().display(getInventory(), 0); getSecondBottleComponent().display(getInventory(), 1); @@ -89,11 +132,13 @@ public void show(@NotNull HumanEntity humanEntity) { getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -103,7 +148,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public BrewingStandGui copy() { - BrewingStandGui gui = new BrewingStandGui(getTitle()); + BrewingStandGui gui = new BrewingStandGui(getTitleHolder(), super.plugin); gui.firstBottleComponent = firstBottleComponent.copy(); gui.secondBottleComponent = secondBottleComponent.copy(); @@ -146,17 +191,40 @@ public boolean isPlayerInventoryUsed() { return getPlayerInventoryComponent().hasItem(); } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - Inventory inventory = Bukkit.createInventory(this, InventoryType.BREWING, title); + public Inventory createInventory() { + Inventory inventory = getTitleHolder().asInventoryTitle(this, InventoryType.BREWING); addInventory(inventory, this); return inventory; } + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + /** * Gets the inventory component representing the first bottle * @@ -234,19 +302,22 @@ public InventoryComponent getPlayerInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded brewing stand gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static BrewingStandGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static BrewingStandGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -258,11 +329,13 @@ public static BrewingStandGui load(@NotNull Object instance, @NotNull InputStrea * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded brewing stand gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static BrewingStandGui load(@NotNull Object instance, @NotNull Element element) { + public static BrewingStandGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } @@ -318,9 +391,36 @@ public static BrewingStandGui load(@NotNull Object instance, @NotNull Element el throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return brewingStandGui; } + + /** + * Loads a brewing stand gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded brewing stand gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static BrewingStandGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(BrewingStandGui.class)); + } + + /** + * Loads a brewing stand gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded brewing stand gui + * @since 0.8.0 + */ + @NotNull + public static BrewingStandGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(BrewingStandGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/CartographyTableGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/CartographyTableGui.java index 14e2d9e0f..84c547042 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/CartographyTableGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/CartographyTableGui.java @@ -1,8 +1,11 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; import com.github.stefvanschie.inventoryframework.util.version.Version; import com.github.stefvanschie.inventoryframework.util.version.VersionMatcher; @@ -13,6 +16,7 @@ import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -27,13 +31,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a cartography table * * @since 0.8.0 */ -public class CartographyTableGui extends NamedGui { +public class CartographyTableGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the map @@ -77,15 +83,52 @@ public CartographyTableGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public CartographyTableGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new cartography table gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #CartographyTableGui(String) + * @since 0.10.8 + */ + public CartographyTableGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new cartography table gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #CartographyTableGui(TextHolder) + * @since 0.10.8 + */ + public CartographyTableGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { if (!(humanEntity instanceof Player)) { throw new IllegalArgumentException("Cartography tables can only be opened by players"); } - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getMapComponent().display(getInventory(), 0); getPaperComponent().display(getInventory(), 1); @@ -93,21 +136,26 @@ public void show(@NotNull HumanEntity humanEntity) { getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } - cartographyTableInventory.openInventory((Player) humanEntity, getTitle(), getTopItems()); + //also let Bukkit know that we opened an inventory + humanEntity.openInventory(getInventory()); + + cartographyTableInventory.openInventory((Player) humanEntity, getTitleHolder(), getTopItems()); } @NotNull @Contract(pure = true) @Override public CartographyTableGui copy() { - CartographyTableGui gui = new CartographyTableGui(getTitle()); + CartographyTableGui gui = new CartographyTableGui(getTitleHolder(), super.plugin); gui.mapComponent = mapComponent.copy(); gui.paperComponent = paperComponent.copy(); @@ -138,6 +186,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -147,8 +205,21 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.CARTOGRAPHY, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.CARTOGRAPHY); + } + + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); } /** @@ -165,8 +236,8 @@ public void handleClickEvent(@NotNull InventoryClickEvent event) { cartographyTableInventory.sendItems(player, getTopItems()); } else if (slot >= 0 && slot <= 2) { //the client rejects the output item if send immediately - Bukkit.getScheduler().runTask(JavaPlugin.getProvidingPlugin(getClass()), () -> - cartographyTableInventory.sendItems(player, getTopItems())); + Bukkit.getScheduler().runTask(super.plugin, () -> + cartographyTableInventory.sendItems(player, getTopItems())); if (event.isCancelled()) { cartographyTableInventory.clearCursor(player); @@ -243,19 +314,22 @@ private ItemStack[] getTopItems() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded cartography table gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static CartographyTableGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static CartographyTableGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -267,16 +341,18 @@ public static CartographyTableGui load(@NotNull Object instance, @NotNull InputS * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded cartography table gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static CartographyTableGui load(@NotNull Object instance, @NotNull Element element) { + public static CartographyTableGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - CartographyTableGui cartographyTableGui = new CartographyTableGui(element.getAttribute("title")); + CartographyTableGui cartographyTableGui = new CartographyTableGui(element.getAttribute("title"), plugin); cartographyTableGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -321,9 +397,36 @@ public static CartographyTableGui load(@NotNull Object instance, @NotNull Elemen throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return cartographyTableGui; } + + /** + * Loads a cartography table gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded cartography table gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static CartographyTableGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(CartographyTableGui.class)); + } + + /** + * Loads a cartography table gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded cartography table gui + * @since 0.8.0 + */ + @NotNull + public static CartographyTableGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(CartographyTableGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/ChestGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/ChestGui.java index 64930e34f..9647388bd 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/ChestGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/ChestGui.java @@ -1,15 +1,20 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.MergedGui; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; import com.github.stefvanschie.inventoryframework.pane.Pane; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,6 +28,7 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -33,7 +39,7 @@ * * @since 0.8.0 */ -public class ChestGui extends NamedGui implements MergedGui { +public class ChestGui extends NamedGui implements MergedGui, InventoryBased { /** * Represents the inventory component for the entire gui @@ -42,9 +48,9 @@ public class ChestGui extends NamedGui implements MergedGui { private InventoryComponent inventoryComponent; /** - * The amount of rows this gui has + * Whether the amount of rows are dirty i.e. has been changed */ - private int rows; + private boolean dirtyRows = false; /** * Constructs a new chest GUI @@ -54,7 +60,44 @@ public class ChestGui extends NamedGui implements MergedGui { * @since 0.8.0 */ public ChestGui(int rows, @NotNull String title) { - super(title); + this(rows, StringHolder.of(title), JavaPlugin.getProvidingPlugin(ChestGui.class)); + } + + /** + * Constructs a new chest GUI + * + * @param rows the amount of rows this gui should contain, in range 1..6. + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public ChestGui(int rows, @NotNull TextHolder title) { + this(rows, title, JavaPlugin.getProvidingPlugin(ChestGui.class)); + } + + /** + * Constructs a new chest gui for the given {@code plugin}. + * + * @param rows the amount of rows this gui should contain, in range 1..6. + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #ChestGui(int, String) + * @since 0.10.8 + */ + public ChestGui(int rows, @NotNull String title, @NotNull Plugin plugin) { + this(rows, StringHolder.of(title), plugin); + } + + /** + * Constructs a new chest gui for the given {@code plugin}. + * + * @param rows the amount of rows this gui should contain, in range 1..6. + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #ChestGui(int, TextHolder) + * @since 0.10.8 + */ + public ChestGui(int rows, @NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); if (!(rows >= 1 && rows <= 6)) { throw new IllegalArgumentException("Rows should be between 1 and 6"); @@ -65,9 +108,14 @@ public ChestGui(int rows, @NotNull String title) { @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); + if (isDirty() || dirtyRows) { + this.inventory = createInventory(); + this.dirtyRows = false; - getHumanEntityCache().store(humanEntity); + markChanges(); + } + + getInventory().clear(); int height = getInventoryComponent().getHeight(); @@ -79,11 +127,13 @@ public void show(@NotNull HumanEntity humanEntity) { topComponent.placeItems(getInventory(), 0); if (bottomComponent.hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } bottomComponent.placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -93,7 +143,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public ChestGui copy() { - ChestGui gui = new ChestGui(getRows(), getTitle()); + ChestGui gui = new ChestGui(getRows(), getTitleHolder(), super.plugin); gui.inventoryComponent = inventoryComponent.copy(); @@ -130,14 +180,24 @@ public void setRows(int rows) { throw new IllegalArgumentException("Rows should be between 1 and 6"); } - this.inventoryComponent = new InventoryComponent(9, rows + 4); + InventoryComponent inventoryComponent = new InventoryComponent(9, rows + 4); - //copy the viewers - List viewers = getViewers(); + for (Pane pane : this.inventoryComponent.getPanes()) { + inventoryComponent.addPane(pane); + } - this.inventory = Bukkit.createInventory(this, rows * 9, getTitle()); + this.inventoryComponent = inventoryComponent; + this.dirtyRows = true; + } - viewers.forEach(humanEntity -> humanEntity.openInventory(inventory)); + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; } @Override @@ -162,8 +222,8 @@ public Collection getItems() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, getRows() * 9, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, getRows() * 9); } /** @@ -177,6 +237,19 @@ public int getRows() { return getInventoryComponent().getHeight() - 4; } + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + @NotNull @Contract(pure = true) @Override @@ -189,19 +262,21 @@ public InventoryComponent getInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded chest gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static ChestGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static ChestGui load(@NotNull Object instance, @NotNull InputStream inputStream, @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -213,11 +288,13 @@ public static ChestGui load(@NotNull Object instance, @NotNull InputStream input * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded chest gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static ChestGui load(@NotNull Object instance, @NotNull Element element) { + public static ChestGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } @@ -234,7 +311,7 @@ public static ChestGui load(@NotNull Object instance, @NotNull Element element) throw new XMLLoadException("Rows attribute is not an integer", exception); } - ChestGui chestGui = new ChestGui(rows, element.getAttribute("title")); + ChestGui chestGui = new ChestGui(rows, element.getAttribute("title"), plugin); chestGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -254,9 +331,9 @@ public static ChestGui load(@NotNull Object instance, @NotNull Element element) InventoryComponent inventoryComponent = chestGui.getInventoryComponent(); if (componentElement.getTagName().equalsIgnoreCase("component")) { - inventoryComponent.load(instance, componentElement); + inventoryComponent.load(instance, componentElement, plugin); } else { - inventoryComponent.load(instance, element); + inventoryComponent.load(instance, element, plugin); } break; @@ -264,4 +341,31 @@ public static ChestGui load(@NotNull Object instance, @NotNull Element element) return chestGui; } + + /** + * Loads a chest gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded chest gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static ChestGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(ChestGui.class)); + } + + /** + * Loads a chest gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded chest gui + * @since 0.8.0 + */ + @NotNull + public static ChestGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(ChestGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/CrafterGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/CrafterGui.java new file mode 100644 index 000000000..f661d6880 --- /dev/null +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/CrafterGui.java @@ -0,0 +1,326 @@ +package com.github.stefvanschie.inventoryframework.gui.type; + +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; +import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; +import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a gui in the form of a crafter. + * + * @since 0.10.13 + */ +public class CrafterGui extends NamedGui implements InventoryBased { + + /** + * Represents the inventory component for the input + */ + @NotNull + private InventoryComponent inputComponent = new InventoryComponent(3, 3); + + /** + * Represents the inventory component for the player inventory + */ + @NotNull + private InventoryComponent playerInventoryComponent = new InventoryComponent(9, 4); + + /** + * Constructs a new crafter gui. + * + * @param title the title/name of this gui + * @since 0.10.13 + */ + public CrafterGui(@NotNull String title) { + super(title); + } + + /** + * Constructs a new crafter gui. + * + * @param title the title/name of this gui + * @since 0.10.13 + */ + public CrafterGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new crafter gui for the given {@code plugin}. + * + * @param title the title/name of this gui + * @param plugin the owning plugin of this gui + * @see #CrafterGui(String) + * @since 0.10.13 + */ + public CrafterGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new crafter gui for the given {@code plugin}. + * + * @param title the title/name of this gui + * @param plugin the owning plugin of this gui + * @see #CrafterGui(TextHolder) + * @since 0.10.13 + */ + public CrafterGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + + @Override + public void show(@NotNull HumanEntity humanEntity) { + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } + + getInventory().clear(); + + getInputComponent().display(getInventory(), 0); + getPlayerInventoryComponent().display(); + + if (getPlayerInventoryComponent().hasItem()) { + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } + + getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); + } + + humanEntity.openInventory(getInventory()); + } + + @NotNull + @Contract(pure = true) + @Override + public CrafterGui copy() { + CrafterGui gui = new CrafterGui(getTitleHolder(), super.plugin); + + gui.inputComponent = inputComponent.copy(); + gui.playerInventoryComponent = playerInventoryComponent.copy(); + + gui.setOnTopClick(this.onTopClick); + gui.setOnBottomClick(this.onBottomClick); + gui.setOnGlobalClick(this.onGlobalClick); + gui.setOnOutsideClick(this.onOutsideClick); + gui.setOnClose(this.onClose); + + return gui; + } + + @Override + public void click(@NotNull InventoryClickEvent event) { + int rawSlot = event.getRawSlot(); + + if (rawSlot >= 0 && rawSlot <= 8) { + getInputComponent().click(this, event, rawSlot); + } else { + getPlayerInventoryComponent().click(this, event, rawSlot - 9); + } + } + + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + + @Contract(pure = true) + @Override + public boolean isPlayerInventoryUsed() { + return getPlayerInventoryComponent().hasItem(); + } + + @NotNull + @Contract(pure = true) + @Override + public Inventory createInventory() { + //noinspection UnstableApiUsage + Inventory inventory = getTitleHolder().asInventoryTitle(this, InventoryType.CRAFTER); + + addInventory(inventory, this); + + return inventory; + } + + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + + /** + * Gets the inventory component representing the input. + * + * @return the input component + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + public InventoryComponent getInputComponent() { + return inputComponent; + } + + /** + * Gets the inventory component representing the player inventory. + * + * @return the player inventory component + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + public InventoryComponent getPlayerInventoryComponent() { + return playerInventoryComponent; + } + + /** + * Loads a crafter gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui + * @return the loaded crafter gui + * @see #load(Object, InputStream) + * @since 0.10.13 + */ + @Nullable + @Contract(pure = true) + public static CrafterGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { + try { + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); + Element documentElement = document.getDocumentElement(); + + documentElement.normalize(); + + return load(instance, documentElement, plugin); + } catch (SAXException | ParserConfigurationException | IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Loads a crafter gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui + * @return the loaded crafter gui + * @since 0.10.13 + */ + @NotNull + public static CrafterGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { + if (!element.hasAttribute("title")) { + throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); + } + + CrafterGui crafterGui = new CrafterGui(element.getAttribute("title"), plugin); + crafterGui.initializeOrThrow(instance, element); + + if (element.hasAttribute("populate")) { + return crafterGui; + } + + NodeList childNodes = element.getChildNodes(); + + for (int index = 0; index < childNodes.getLength(); index++) { + Node item = childNodes.item(index); + + if (item.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + Element componentElement = (Element) item; + + if (!componentElement.getTagName().equalsIgnoreCase("component")) { + throw new XMLLoadException("Gui element contains non-component tags"); + } + + if (!componentElement.hasAttribute("name")) { + throw new XMLLoadException("Component tag does not have a name specified"); + } + + InventoryComponent component; + + switch (componentElement.getAttribute("name")) { + case "input": + component = crafterGui.getInputComponent(); + break; + case "player-inventory": + component = crafterGui.getPlayerInventoryComponent(); + break; + default: + throw new XMLLoadException("Unknown component name"); + } + + component.load(instance, componentElement, plugin); + } + + return crafterGui; + } + + /** + * Loads a crafter gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded crafter gui + * @since 0.10.13 + */ + @Nullable + @Contract(pure = true) + public static CrafterGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(CrafterGui.class)); + } + + /** + * Loads a crafter gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded crafting table gui + * @since 0.10.13 + */ + @NotNull + public static CrafterGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(CrafterGui.class)); + } +} diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/CraftingTableGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/CraftingTableGui.java index 735260584..6082f4f00 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/CraftingTableGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/CraftingTableGui.java @@ -1,13 +1,17 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,13 +25,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a crafting table * * @since 0.8.0 */ -public class CraftingTableGui extends NamedGui { +public class CraftingTableGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the input @@ -57,22 +63,61 @@ public CraftingTableGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public CraftingTableGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new crafting table gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #CraftingTableGui(String) + * @since 0.10.8 + */ + public CraftingTableGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new crafting table gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #CraftingTableGui(TextHolder) + * @since 0.10.8 + */ + public CraftingTableGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getOutputComponent().display(getInventory(), 0); getInputComponent().display(getInventory(), 1); getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -82,7 +127,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public CraftingTableGui copy() { - CraftingTableGui gui = new CraftingTableGui(getTitle()); + CraftingTableGui gui = new CraftingTableGui(getTitleHolder(), super.plugin); gui.inputComponent = inputComponent.copy(); gui.outputComponent = outputComponent.copy(); @@ -110,6 +155,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -119,8 +174,21 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.WORKBENCH, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.WORKBENCH); + } + + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); } /** @@ -164,19 +232,22 @@ public InventoryComponent getPlayerInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded crafting table gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static CraftingTableGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static CraftingTableGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -188,16 +259,17 @@ public static CraftingTableGui load(@NotNull Object instance, @NotNull InputStre * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded crafting table gui - * @since 0.8.0 + * @since 0.10.8 */ @NotNull - public static CraftingTableGui load(@NotNull Object instance, @NotNull Element element) { + public static CraftingTableGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - CraftingTableGui craftingTableGui = new CraftingTableGui(element.getAttribute("title")); + CraftingTableGui craftingTableGui = new CraftingTableGui(element.getAttribute("title"), plugin); craftingTableGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -239,9 +311,36 @@ public static CraftingTableGui load(@NotNull Object instance, @NotNull Element e throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return craftingTableGui; } + + /** + * Loads a crafting table gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded crafting table gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static CraftingTableGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(CraftingTableGui.class)); + } + + /** + * Loads a crafting table gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded crafting table gui + * @since 0.8.0 + */ + @NotNull + public static CraftingTableGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(CraftingTableGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/DispenserGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/DispenserGui.java index dafdd3262..1a306f76e 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/DispenserGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/DispenserGui.java @@ -1,13 +1,17 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,13 +25,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a dispenser * * @since 0.8.0 */ -public class DispenserGui extends NamedGui { +public class DispenserGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the contents @@ -51,21 +57,60 @@ public DispenserGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public DispenserGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new dispenser gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #DispenserGui(String) + * @since 0.10.8 + */ + public DispenserGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new dispenser gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #DispenserGui(TextHolder) + * @since 0.10.8 + */ + public DispenserGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getContentsComponent().display(getInventory(), 0); getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -75,7 +120,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public DispenserGui copy() { - DispenserGui gui = new DispenserGui(getTitle()); + DispenserGui gui = new DispenserGui(getTitleHolder(), super.plugin); gui.contentsComponent = contentsComponent.copy(); gui.playerInventoryComponent = playerInventoryComponent.copy(); @@ -100,6 +145,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -109,14 +164,27 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - Inventory inventory = Bukkit.createInventory(this, InventoryType.DISPENSER, title); + public Inventory createInventory() { + Inventory inventory = getTitleHolder().asInventoryTitle(this, InventoryType.DISPENSER); addInventory(inventory, this); return inventory; } + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + /** * Gets the inventory component representing the contents * @@ -146,19 +214,22 @@ public InventoryComponent getPlayerInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded dispenser gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static DispenserGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static DispenserGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -170,16 +241,18 @@ public static DispenserGui load(@NotNull Object instance, @NotNull InputStream i * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded dispenser gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static DispenserGui load(@NotNull Object instance, @NotNull Element element) { + public static DispenserGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - DispenserGui dispenserGui = new DispenserGui(element.getAttribute("title")); + DispenserGui dispenserGui = new DispenserGui(element.getAttribute("title"), plugin); dispenserGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -218,9 +291,36 @@ public static DispenserGui load(@NotNull Object instance, @NotNull Element eleme throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return dispenserGui; } + + /** + * Loads a dispenser gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded dispenser gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static DispenserGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(DispenserGui.class)); + } + + /** + * Loads a dispenser gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded dispenser gui + * @since 0.8.0 + */ + @NotNull + public static DispenserGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(DispenserGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/DropperGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/DropperGui.java index 2cbf45cd1..9d45d08da 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/DropperGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/DropperGui.java @@ -1,13 +1,17 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,13 +25,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a dropper * * @since 0.8.0 */ -public class DropperGui extends NamedGui { +public class DropperGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the contents @@ -51,21 +57,60 @@ public DropperGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public DropperGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new dropper gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #DropperGui(String) + * @since 0.10.8 + */ + public DropperGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new dropper gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #DropperGui(TextHolder) + * @since 0.10.8 + */ + public DropperGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getContentsComponent().display(getInventory(), 0); getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -75,7 +120,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public DropperGui copy() { - DropperGui gui = new DropperGui(getTitle()); + DropperGui gui = new DropperGui(getTitleHolder(), super.plugin); gui.contentsComponent = contentsComponent.copy(); gui.playerInventoryComponent = playerInventoryComponent.copy(); @@ -100,6 +145,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -109,14 +164,27 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - Inventory inventory = Bukkit.createInventory(this, InventoryType.DROPPER, title); + public Inventory createInventory() { + Inventory inventory = getTitleHolder().asInventoryTitle(this, InventoryType.DROPPER); addInventory(inventory, this); return inventory; } + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + /** * Gets the inventory component representing the contents * @@ -146,19 +214,21 @@ public InventoryComponent getPlayerInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded dropper gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static DropperGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static DropperGui load(@NotNull Object instance, @NotNull InputStream inputStream, @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -170,16 +240,18 @@ public static DropperGui load(@NotNull Object instance, @NotNull InputStream inp * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded dropper gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static DropperGui load(@NotNull Object instance, @NotNull Element element) { + public static DropperGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - DropperGui dropperGui = new DropperGui(element.getAttribute("title")); + DropperGui dropperGui = new DropperGui(element.getAttribute("title"), plugin); dropperGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -218,9 +290,36 @@ public static DropperGui load(@NotNull Object instance, @NotNull Element element throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return dropperGui; } + + /** + * Loads a dropper gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded dropper gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static DropperGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(DropperGui.class)); + } + + /** + * Loads a dropper gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded dropper gui + * @since 0.8.0 + */ + @NotNull + public static DropperGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(DropperGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/EnchantingTableGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/EnchantingTableGui.java index d4e771b4d..a49473c4f 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/EnchantingTableGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/EnchantingTableGui.java @@ -1,18 +1,22 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; import com.github.stefvanschie.inventoryframework.util.version.Version; import com.github.stefvanschie.inventoryframework.util.version.VersionMatcher; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,13 +30,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of an enchanting table * * @since 0.8.0 */ -public class EnchantingTableGui extends NamedGui { +public class EnchantingTableGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the input @@ -63,35 +69,77 @@ public EnchantingTableGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public EnchantingTableGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new enchanting table gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #EnchantingTableGui(String) + * @since 0.10.8 + */ + public EnchantingTableGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new enchanting table gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #EnchantingTableGui(TextHolder) + * @since 0.10.8 + */ + public EnchantingTableGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { if (!(humanEntity instanceof Player)) { throw new IllegalArgumentException("Enchanting tables can only be opened by players"); } - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getInputComponent().display(getInventory(), 0); getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } - enchantingTableInventory.openInventory((Player) humanEntity, getTitle(), getTopItems()); + //also let Bukkit know that we opened an inventory + humanEntity.openInventory(getInventory()); + + enchantingTableInventory.openInventory((Player) humanEntity, getTitleHolder(), getTopItems()); } @NotNull @Contract(pure = true) @Override public EnchantingTableGui copy() { - EnchantingTableGui gui = new EnchantingTableGui(getTitle()); + EnchantingTableGui gui = new EnchantingTableGui(getTitleHolder(), super.plugin); gui.inputComponent = inputComponent.copy(); gui.playerInventoryComponent = playerInventoryComponent.copy(); @@ -116,6 +164,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -125,8 +183,8 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.ENCHANTING, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.ENCHANTING); } /** @@ -148,6 +206,19 @@ public void handleClickEvent(@NotNull InventoryClickEvent event) { } } + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + /** * Gets the inventory component representing the input * @@ -192,19 +263,22 @@ private ItemStack[] getTopItems() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded enchanting table gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static EnchantingTableGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static EnchantingTableGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -216,16 +290,18 @@ public static EnchantingTableGui load(@NotNull Object instance, @NotNull InputSt * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded enchanting table gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static EnchantingTableGui load(@NotNull Object instance, @NotNull Element element) { + public static EnchantingTableGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - EnchantingTableGui enchantingTableGui = new EnchantingTableGui(element.getAttribute("title")); + EnchantingTableGui enchantingTableGui = new EnchantingTableGui(element.getAttribute("title"), plugin); enchantingTableGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -264,9 +340,36 @@ public static EnchantingTableGui load(@NotNull Object instance, @NotNull Element throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return enchantingTableGui; } + + /** + * Loads an enchanting table gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded enchanting table gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static EnchantingTableGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(EnchantingTableGui.class)); + } + + /** + * Loads an enchanting table gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded enchanting table gui + * @since 0.8.0 + */ + @NotNull + public static EnchantingTableGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(EnchantingTableGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/EnderChestGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/EnderChestGui.java index db47c007e..9e4a91835 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/EnderChestGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/EnderChestGui.java @@ -1,16 +1,20 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.MergedGui; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; import com.github.stefvanschie.inventoryframework.pane.Pane; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -24,6 +28,7 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -33,7 +38,7 @@ * * @since 0.8.0 */ -public class EnderChestGui extends NamedGui implements MergedGui { +public class EnderChestGui extends NamedGui implements MergedGui, InventoryBased { /** * Represents the inventory component for the entire gui @@ -51,11 +56,48 @@ public EnderChestGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public EnderChestGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new ender chest gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #EnderChestGui(String) + * @since 0.10.8 + */ + public EnderChestGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new ender chest gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #EnderChestGui(TextHolder) + * @since 0.10.8 + */ + public EnderChestGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); int height = getInventoryComponent().getHeight(); @@ -65,14 +107,15 @@ public void show(@NotNull HumanEntity humanEntity) { InventoryComponent bottomComponent = getInventoryComponent().excludeRows(0, height - 5); topComponent.placeItems(getInventory(), 0); - bottomComponent.placeItems(humanEntity.getInventory(), 0); if (bottomComponent.hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } bottomComponent.placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -82,7 +125,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public EnderChestGui copy() { - EnderChestGui gui = new EnderChestGui(getTitle()); + EnderChestGui gui = new EnderChestGui(getTitleHolder(), super.plugin); gui.inventoryComponent = inventoryComponent.copy(); @@ -95,6 +138,16 @@ public EnderChestGui copy() { return gui; } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -128,8 +181,21 @@ public Collection getItems() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.ENDER_CHEST, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.ENDER_CHEST); + } + + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); } @NotNull @@ -144,19 +210,22 @@ public InventoryComponent getInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded ender chest gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static EnderChestGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static EnderChestGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -168,16 +237,18 @@ public static EnderChestGui load(@NotNull Object instance, @NotNull InputStream * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded ender chest gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static EnderChestGui load(@NotNull Object instance, @NotNull Element element) { + public static EnderChestGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - EnderChestGui enderChestGui = new EnderChestGui(element.getAttribute("title")); + EnderChestGui enderChestGui = new EnderChestGui(element.getAttribute("title"), plugin); enderChestGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -197,9 +268,9 @@ public static EnderChestGui load(@NotNull Object instance, @NotNull Element elem InventoryComponent inventoryComponent = enderChestGui.getInventoryComponent(); if (componentElement.getTagName().equalsIgnoreCase("component")) { - inventoryComponent.load(instance, componentElement); + inventoryComponent.load(instance, componentElement, plugin); } else { - inventoryComponent.load(instance, element); + inventoryComponent.load(instance, element, plugin); } break; @@ -207,4 +278,31 @@ public static EnderChestGui load(@NotNull Object instance, @NotNull Element elem return enderChestGui; } + + /** + * Loads an ender chest gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded ender chest gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static EnderChestGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(EnderChestGui.class)); + } + + /** + * Loads an ender chest gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded ender chest gui + * @since 0.8.0 + */ + @NotNull + public static EnderChestGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(EnderChestGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/FurnaceGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/FurnaceGui.java index 674aa2dae..d079e2628 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/FurnaceGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/FurnaceGui.java @@ -1,13 +1,17 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,13 +25,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a furnace * * @since 0.8.0 */ -public class FurnaceGui extends NamedGui { +public class FurnaceGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the ingredient @@ -63,11 +69,48 @@ public FurnaceGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public FurnaceGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new furnace gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #FurnaceGui(String) + * @since 0.10.8 + */ + public FurnaceGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new furnace gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #FurnaceGui(TextHolder) + * @since 0.10.8 + */ + public FurnaceGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getIngredientComponent().display(getInventory(), 0); getFuelComponent().display(getInventory(), 1); @@ -75,11 +118,13 @@ public void show(@NotNull HumanEntity humanEntity) { getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -89,7 +134,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public FurnaceGui copy() { - FurnaceGui gui = new FurnaceGui(getTitle()); + FurnaceGui gui = new FurnaceGui(getTitleHolder(), super.plugin); gui.ingredientComponent = ingredientComponent.copy(); gui.fuelComponent = fuelComponent.copy(); @@ -120,6 +165,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -129,14 +184,27 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - Inventory inventory = Bukkit.createInventory(this, InventoryType.FURNACE, title); + public Inventory createInventory() { + Inventory inventory = getTitleHolder().asInventoryTitle(this, InventoryType.FURNACE); addInventory(inventory, this); return inventory; } + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + /** * Gets the inventory component representing the ingredient * @@ -190,19 +258,21 @@ public InventoryComponent getPlayerInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded furnace gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static FurnaceGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static FurnaceGui load(@NotNull Object instance, @NotNull InputStream inputStream, @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -214,16 +284,18 @@ public static FurnaceGui load(@NotNull Object instance, @NotNull InputStream inp * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded furnace gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static FurnaceGui load(@NotNull Object instance, @NotNull Element element) { + public static FurnaceGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - FurnaceGui furnaceGui = new FurnaceGui(element.getAttribute("title")); + FurnaceGui furnaceGui = new FurnaceGui(element.getAttribute("title"), plugin); furnaceGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -268,9 +340,36 @@ public static FurnaceGui load(@NotNull Object instance, @NotNull Element element throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return furnaceGui; } + + /** + * Loads a furnace gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded furnace gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static FurnaceGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(FurnaceGui.class)); + } + + /** + * Loads a furnace gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded furnace gui + * @since 0.8.0 + */ + @NotNull + public static FurnaceGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(FurnaceGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/GrindstoneGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/GrindstoneGui.java index a7c665c6e..b5be1f137 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/GrindstoneGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/GrindstoneGui.java @@ -1,18 +1,22 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; import com.github.stefvanschie.inventoryframework.util.version.Version; import com.github.stefvanschie.inventoryframework.util.version.VersionMatcher; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,13 +30,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a grindstone * * @since 0.8.0 */ -public class GrindstoneGui extends NamedGui { +public class GrindstoneGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the items @@ -69,36 +75,77 @@ public GrindstoneGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public GrindstoneGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new grindstone gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #GrindstoneGui(String) + * @since 0.10.8 + */ + public GrindstoneGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new grindstone gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #GrindstoneGui(TextHolder) + * @since 0.10.8 + */ + public GrindstoneGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { if (!(humanEntity instanceof Player)) { throw new IllegalArgumentException("Grindstones can only be opened by players"); } - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getItemsComponent().display(getInventory(), 0); getResultComponent().display(getInventory(), 2); getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } - grindstoneInventory.openInventory((Player) humanEntity, getTitle(), getTopItems()); + Inventory inventory = grindstoneInventory.openInventory((Player) humanEntity, getTitleHolder(), getTopItems()); + + addInventory(inventory, this); } @NotNull @Contract(pure = true) @Override public GrindstoneGui copy() { - GrindstoneGui gui = new GrindstoneGui(getTitle()); + GrindstoneGui gui = new GrindstoneGui(getTitleHolder(), super.plugin); gui.itemsComponent = itemsComponent.copy(); gui.resultComponent = resultComponent.copy(); @@ -126,6 +173,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -135,8 +192,8 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.GRINDSTONE, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.GRINDSTONE); } /** @@ -144,15 +201,17 @@ public Inventory createInventory(@NotNull String title) { * * @param event the event to handle * @since 0.8.0 + * @deprecated no longer used internally */ + @Deprecated public void handleClickEvent(@NotNull InventoryClickEvent event) { int slot = event.getRawSlot(); Player player = (Player) event.getWhoClicked(); if (slot >= 3 && slot <= 38) { - grindstoneInventory.sendItems(player, getTopItems()); + grindstoneInventory.sendItems(player, getTopItems(), event.getCurrentItem()); } else if (slot >= 0 && slot <= 2) { - grindstoneInventory.sendItems(player, getTopItems()); + grindstoneInventory.sendItems(player, getTopItems(), event.getCurrentItem()); if (event.isCancelled()) { grindstoneInventory.clearCursor(player); @@ -160,6 +219,19 @@ public void handleClickEvent(@NotNull InventoryClickEvent event) { } } + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + /** * Gets the inventory component representing the items * @@ -217,19 +289,22 @@ private ItemStack[] getTopItems() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded furnace gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static GrindstoneGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static GrindstoneGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -241,16 +316,18 @@ public static GrindstoneGui load(@NotNull Object instance, @NotNull InputStream * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded grindstone gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static GrindstoneGui load(@NotNull Object instance, @NotNull Element element) { + public static GrindstoneGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - GrindstoneGui grindstoneGui = new GrindstoneGui(element.getAttribute("title")); + GrindstoneGui grindstoneGui = new GrindstoneGui(element.getAttribute("title"), plugin); grindstoneGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -292,9 +369,36 @@ public static GrindstoneGui load(@NotNull Object instance, @NotNull Element elem throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return grindstoneGui; } + + /** + * Loads a grindstone gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded furnace gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static GrindstoneGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(GrindstoneGui.class)); + } + + /** + * Loads a grindstone gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded grindstone gui + * @since 0.8.0 + */ + @NotNull + public static GrindstoneGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(GrindstoneGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/HopperGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/HopperGui.java index b33be09b7..8136f9328 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/HopperGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/HopperGui.java @@ -1,13 +1,17 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,13 +25,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a hopper * * @since 0.8.0 */ -public class HopperGui extends NamedGui { +public class HopperGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the slots @@ -51,21 +57,60 @@ public HopperGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public HopperGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new hopper gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #HopperGui(String) + * @since 0.10.8 + */ + public HopperGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new hopper gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #HopperGui(TextHolder) + * @since 0.10.8 + */ + public HopperGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getSlotsComponent().display(getInventory(), 0); getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -75,7 +120,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public HopperGui copy() { - HopperGui gui = new HopperGui(getTitle()); + HopperGui gui = new HopperGui(getTitleHolder(), super.plugin); gui.slotsComponent = slotsComponent.copy(); gui.playerInventoryComponent = playerInventoryComponent.copy(); @@ -100,6 +145,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -109,14 +164,27 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - Inventory inventory = Bukkit.createInventory(this, InventoryType.HOPPER, title); + public Inventory createInventory() { + Inventory inventory = getTitleHolder().asInventoryTitle(this, InventoryType.HOPPER); addInventory(inventory, this); return inventory; } + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + /** * Gets the inventory component for the slots * @@ -146,19 +214,21 @@ public InventoryComponent getPlayerInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded hopper gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static HopperGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static HopperGui load(@NotNull Object instance, @NotNull InputStream inputStream, @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -170,16 +240,18 @@ public static HopperGui load(@NotNull Object instance, @NotNull InputStream inpu * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded hopper gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static HopperGui load(@NotNull Object instance, @NotNull Element element) { + public static HopperGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - HopperGui hopperGui = new HopperGui(element.getAttribute("title")); + HopperGui hopperGui = new HopperGui(element.getAttribute("title"), plugin); hopperGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -218,9 +290,36 @@ public static HopperGui load(@NotNull Object instance, @NotNull Element element) throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return hopperGui; } + + /** + * Loads a hopper gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded hopper gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static HopperGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(HopperGui.class)); + } + + /** + * Loads a hopper gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded hopper gui + * @since 0.8.0 + */ + @NotNull + public static HopperGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(HopperGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/MerchantGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/MerchantGui.java new file mode 100644 index 000000000..480daf510 --- /dev/null +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/MerchantGui.java @@ -0,0 +1,592 @@ +package com.github.stefvanschie.inventoryframework.gui.type; + +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; +import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; +import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; +import com.github.stefvanschie.inventoryframework.pane.Pane; +import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil; +import com.github.stefvanschie.inventoryframework.util.XMLUtil; +import com.github.stefvanschie.inventoryframework.util.version.Version; +import com.github.stefvanschie.inventoryframework.util.version.VersionMatcher; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.TradeSelectEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Merchant; +import org.bukkit.inventory.MerchantRecipe; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.InputStream; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +/** + * Represents a gui in the form of a merchant. + * + * @since 0.10.0 + */ +public class MerchantGui extends NamedGui { + + /** + * The consumer that will be called once a players selects a trade listed + * on the left side of the gui + */ + private Consumer onTradeSelect; + + /** + * Represents the inventory component for the input + */ + @NotNull + private InventoryComponent inputComponent = new InventoryComponent(2, 1); + + /** + * Represents the inventory component for the player inventory + */ + @NotNull + private InventoryComponent playerInventoryComponent = new InventoryComponent(9, 4); + + /** + * The merchant holding the trades and inventory + */ + @NotNull + private Merchant merchant; + + /** + * The human entities viewing this gui + */ + @NotNull + private final List viewers = new ArrayList<>(); + + /** + * The trades of this merchant with their price differences. The differences are the difference between the new + * price and the original price. + */ + @NotNull + private final List> trades = new ArrayList<>(); + + /** + * The experience of this merchant. Values below zero indicate that the experience should be hidden. + */ + private int experience = -1; + + /** + * The level of this merchant. A value of zero indicates this villager doesn't have a level. + */ + private int level = 0; + + /** + * The internal merchant inventory + */ + @NotNull + private final MerchantInventory merchantInventory = VersionMatcher.newMerchantInventory(Version.getVersion()); + + /** + * Creates a merchant gui with the given title. + * + * @param title the title + * @since 0.10.0 + */ + public MerchantGui(@NotNull String title) { + this(StringHolder.of(title)); + } + + /** + * Creates a merchant gui with the given title. + * + * @param title the title + * @since 0.10.0 + */ + public MerchantGui(@NotNull TextHolder title) { + this(title, JavaPlugin.getProvidingPlugin(MerchantGui.class)); + } + + /** + * Constructs a new merchant gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #MerchantGui(String) + * @since 0.10.8 + */ + public MerchantGui(@NotNull String title, @NotNull Plugin plugin) { + this(StringHolder.of(title), plugin); + } + + /** + * Constructs a new merchant gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #MerchantGui(TextHolder) + * @since 0.10.8 + */ + public MerchantGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + + this.merchant = getTitleHolder().asMerchantTitle(); + } + + /** + * Set the consumer that should be called whenever a trade is selected + * in this gui. + * + * @param onTradeSelect the consumer that gets called + */ + public void setOnTradeSelect(@Nullable Consumer onTradeSelect) { + this.onTradeSelect = onTradeSelect; + } + + /** + * Calls the consumer (if it's not null) that was specified using {@link #setOnTradeSelect(Consumer)}, + * so the consumer that should be called whenever a trade is selected in this gui. + * Catches and logs all exceptions the consumer might throw. + * + * @param event the event to handle + */ + public void callOnTradeSelect(@NotNull TradeSelectEvent event) { + callCallback(onTradeSelect, event, "onTradeSelect"); + } + + @Override + protected void initializeOrThrow(@NotNull Object instance, @NotNull Element element) { + super.initializeOrThrow(instance, element); + + if (element.hasAttribute("onTradeSelect")) { + setOnTradeSelect(XMLUtil.loadOnEventAttribute(instance, + element, TradeSelectEvent.class, "onTradeSelect")); + } + } + + @Override + public void show(@NotNull HumanEntity humanEntity) { + if (!(humanEntity instanceof Player)) { + throw new IllegalArgumentException("Merchants can only be opened by players"); + } + + if (isDirty()) { + this.merchant = getTitleHolder().asMerchantTitle(); + markChanges(); + } + + InventoryView view = humanEntity.openMerchant(merchant, true); + + if (view == null) { + throw new IllegalStateException("Merchant could not be opened"); + } + + Inventory inventory = InventoryViewUtil.getInstance().getTopInventory(view); + + addInventory(inventory, this); + + inventory.clear(); + + getInputComponent().display(inventory, 0); + getPlayerInventoryComponent().display(); + + if (getPlayerInventoryComponent().hasItem()) { + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } + + getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); + } + + this.viewers.add(humanEntity); + + Player player = (Player) humanEntity; + + if (this.experience >= 0 || this.level > 0) { + this.merchantInventory.sendMerchantOffers(player, this.trades, this.level, this.experience); + + return; + } + + boolean discount = false; + + for (Map.Entry trade : this.trades) { + if (trade.getValue() != 0) { + this.merchantInventory.sendMerchantOffers(player, this.trades, this.level, this.experience); + + break; + } + } + } + + @NotNull + @Override + public Gui copy() { + MerchantGui gui = new MerchantGui(getTitleHolder(), super.plugin); + + gui.inputComponent = inputComponent.copy(); + gui.playerInventoryComponent = playerInventoryComponent.copy(); + + gui.experience = experience; + gui.level = level; + + for (Map.Entry trade : trades) { + MerchantRecipe originalRecipe = trade.getKey(); + + ItemStack result = originalRecipe.getResult().clone(); + int uses = originalRecipe.getUses(); + int maxUses = originalRecipe.getMaxUses(); + boolean experienceReward = originalRecipe.hasExperienceReward(); + int villagerExperience = originalRecipe.getVillagerExperience(); + float priceMultiplier = originalRecipe.getPriceMultiplier(); + + MerchantRecipe recipe = new MerchantRecipe( + result, uses, maxUses, experienceReward, villagerExperience, priceMultiplier + ); + + for (ItemStack ingredient : originalRecipe.getIngredients()) { + recipe.addIngredient(ingredient.clone()); + } + + gui.trades.add(new AbstractMap.SimpleImmutableEntry<>(recipe, trade.getValue())); + } + + gui.setOnTopClick(this.onTopClick); + gui.setOnBottomClick(this.onBottomClick); + gui.setOnGlobalClick(this.onGlobalClick); + gui.setOnOutsideClick(this.onOutsideClick); + gui.setOnTradeSelect(this.onTradeSelect); + gui.setOnClose(this.onClose); + + return gui; + } + + @Override + public void click(@NotNull InventoryClickEvent event) { + int rawSlot = event.getRawSlot(); + + if (rawSlot >= 0 && rawSlot <= 1) { + getInputComponent().click(this, event, rawSlot); + } else if (rawSlot != 2) { + getPlayerInventoryComponent().click(this, event, rawSlot - 3); + } + } + + /** + * Adds a trade to this gui. The specified discount is the difference between the old price and the new price. For + * example, if a price was decreased from five to two, the discount would be three. + * + * @param recipe the recipe to add + * @param discount the discount + * @since 0.10.1 + */ + public void addTrade(@NotNull MerchantRecipe recipe, int discount) { + this.trades.add(new AbstractMap.SimpleImmutableEntry<>(recipe, -discount)); + + List recipes = new ArrayList<>(this.merchant.getRecipes()); + + recipes.add(recipe); + + this.merchant.setRecipes(recipes); + } + + /** + * Sets the experience of this merchant gui. Setting the experience will make the experience bar visible, even if + * the amount of experience is zero. Note that if the level of this merchant gui has not been set via + * {@link #setLevel(int)} that the experience will always show as zero even when set to something else. Experience + * must be greater than or equal to zero. Attempting to set the experience to below zero will throw an + * {@link IllegalArgumentException}. + * + * @param experience the experience to set + * @since 0.10.1 + * @throws IllegalArgumentException when the experience is below zero + */ + public void setExperience(int experience) { + if (experience < 0) { + throw new IllegalArgumentException("Experience must be greater than or equal to zero"); + } + + this.experience = experience; + } + + /** + * Sets the level of this merchant gui. This is a value between one and five and will visibly change the gui by + * appending the level of the villager to the title. These are displayed as "Novice", "Apprentice", "Journeyman", + * "Expert" and "Master" respectively (when the player's locale is set to English). When an argument is supplied + * that is not within one and five, an {@link IllegalArgumentException} will be thrown. + * + * @param level the numeric level + * @since 0.10.1 + * @throws IllegalArgumentException when the level is not between one and five + */ + public void setLevel(int level) { + if (level < 0 || level > 5) { + throw new IllegalArgumentException("Level must be between one and five"); + } + + this.level = level; + } + + /** + * Adds a trade to this gui. This will not set a discount on the trade. For specifiying discounts, see + * {@link #addTrade(MerchantRecipe, int)}. + * + * @param recipe the recipe to add + * @since 0.10.0 + */ + public void addTrade(@NotNull MerchantRecipe recipe) { + addTrade(recipe, 0); + } + + /** + * Handles a human entity closing this gui. + * + * @param humanEntity the human entity who's closing this gui + * @since 0.10.0 + */ + public void handleClose(@NotNull HumanEntity humanEntity) { + this.viewers.remove(humanEntity); + } + + @Override + public boolean isPlayerInventoryUsed() { + return getPlayerInventoryComponent().hasItem(); + } + + @Contract(pure = true) + @Override + public int getViewerCount() { + return this.viewers.size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(this.viewers); + } + + /** + * Gets the inventory component representing the input + * + * @return the input component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public InventoryComponent getInputComponent() { + return inputComponent; + } + + /** + * Gets the inventory component representing the player inventory + * + * @return the player inventory component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public InventoryComponent getPlayerInventoryComponent() { + return playerInventoryComponent; + } + + /** + * Loads a merchant gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui + * @return the loaded merchant gui + * @see #load(Object, InputStream) + * @since 0.10.8 + */ + @Nullable + @Contract(pure = true) + public static MerchantGui load(@NotNull Object instance, @NotNull InputStream inputStream, @NotNull Plugin plugin) { + try { + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); + Element documentElement = document.getDocumentElement(); + + documentElement.normalize(); + + return load(instance, documentElement, plugin); + } catch (SAXException | ParserConfigurationException | IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Loads a merchant gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui + * @return the loaded merchant gui + * @see #load(Object, Element) + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + public static MerchantGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { + if (!element.hasAttribute("title")) { + throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); + } + + MerchantGui merchantGui = new MerchantGui(element.getAttribute("title"), plugin); + merchantGui.initializeOrThrow(instance, element); + + if (element.hasAttribute("populate")) { + return merchantGui; + } + + NodeList childNodes = element.getChildNodes(); + + for (int index = 0; index < childNodes.getLength(); index++) { + Node item = childNodes.item(index); + + if (item.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + Element nestedElement = (Element) item; + String tagName = nestedElement.getTagName(); + + if (tagName.equalsIgnoreCase("component")) { + if (!nestedElement.hasAttribute("name")) { + throw new XMLLoadException("Component tag does not have a name specified"); + } + + InventoryComponent component; + + switch (nestedElement.getAttribute("name")) { + case "input": + component = merchantGui.getInputComponent(); + break; + case "player-inventory": + component = merchantGui.getPlayerInventoryComponent(); + break; + default: + throw new XMLLoadException("Unknown component name"); + } + + component.load(instance, nestedElement, plugin); + } else if (tagName.equalsIgnoreCase("trade")) { + NodeList tradeNodes = nestedElement.getChildNodes(); + + List ingredients = new ArrayList<>(2); + ItemStack result = null; + + for (int tradeIndex = 0; tradeIndex < tradeNodes.getLength(); tradeIndex++) { + Node tradeNode = tradeNodes.item(tradeIndex); + + if (tradeNode.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + Element tradeElement = (Element) tradeNode; + + if (tradeElement.getTagName().equalsIgnoreCase("ingredient")) { + if (ingredients.size() >= 2) { + throw new XMLLoadException("Too many ingredients specified, must be no more than two"); + } + + NodeList ingredientNodes = tradeElement.getChildNodes(); + + for (int ingredientIndex = 0; ingredientIndex < ingredientNodes.getLength(); ingredientIndex++) { + Node ingredientNode = ingredientNodes.item(ingredientIndex); + + if (ingredientNode.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + ingredients.add(Pane.loadItem(instance, (Element) ingredientNode).getItem()); + } + } else if (tradeElement.getTagName().equalsIgnoreCase("result")) { + NodeList resultNodes = tradeElement.getChildNodes(); + + for (int resultIndex = 0; resultIndex < resultNodes.getLength(); resultIndex++) { + Node resultNode = resultNodes.item(resultIndex); + + if (resultNode.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + if (result != null) { + throw new XMLLoadException("Multiple results specified for the same trade"); + } + + result = Pane.loadItem(instance, (Element) resultNode).getItem(); + } + } else { + throw new XMLLoadException("Trade element is neither an ingredient nor a result"); + } + } + + if (result == null) { + throw new XMLLoadException("Trade must have a result specified"); + } + + if (ingredients.size() < 1) { + throw new XMLLoadException("Trade must have at least one ingredient"); + } + + MerchantRecipe recipe = new MerchantRecipe(result, Integer.MAX_VALUE); + + recipe.setIngredients(ingredients); + + merchantGui.addTrade(recipe); + } else { + throw new XMLLoadException("Nested element is neither a component nor a trade"); + } + } + + return merchantGui; + } + + /** + * Loads a merchant gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded merchant gui + * @since 0.10.0 + */ + @Nullable + @Contract(pure = true) + public static MerchantGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(MerchantGui.class)); + } + + /** + * Loads a merchant gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded merchant gui + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static MerchantGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(MerchantGui.class)); + } +} diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/ModernSmithingTableGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/ModernSmithingTableGui.java new file mode 100644 index 000000000..f0d85a7a0 --- /dev/null +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/ModernSmithingTableGui.java @@ -0,0 +1,404 @@ +package com.github.stefvanschie.inventoryframework.gui.type; + +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; +import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; +import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; +import com.github.stefvanschie.inventoryframework.util.version.Version; +import com.github.stefvanschie.inventoryframework.util.version.VersionMatcher; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +/** + * Represents a gui in the form of a smithing table. This is the modern variant with three input slots, available in + * from Minecraft 1.19.4. + * + * @since 0.10.9 + */ +public class ModernSmithingTableGui extends NamedGui implements InventoryBased { + + /** + * Represents the inventory component for the input + */ + @NotNull + private InventoryComponent inputComponent = new InventoryComponent(3, 1); + + /** + * Represents the inventory component for the result + */ + @NotNull + private InventoryComponent resultComponent = new InventoryComponent(1, 1); + + /** + * Represents the inventory component for the player inventory + */ + @NotNull + private InventoryComponent playerInventoryComponent = new InventoryComponent(9, 4); + + /** + * An internal smithing inventory + */ + @NotNull + private final SmithingTableInventory smithingTableInventory = VersionMatcher.newModernSmithingTableInventory( + Version.getVersion(), this + ); + + /** + * The viewers of this gui + */ + @NotNull + private final Collection viewers = new HashSet<>(); + + /** + * Constructs a new GUI. + * + * @param title the title/name of this gui. + * @since 0.10.9 + */ + public ModernSmithingTableGui(@NotNull String title) { + super(title); + } + + /** + * Constructs a new GUI. + * + * @param title the title/name of this gui. + * @since 0.10.9 + */ + public ModernSmithingTableGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new smithing table gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #ModernSmithingTableGui(String) + * @since 0.10.9 + */ + public ModernSmithingTableGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new smithing table gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #ModernSmithingTableGui(TextHolder) + * @since 0.10.9 + */ + public ModernSmithingTableGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + + @Override + public void show(@NotNull HumanEntity humanEntity) { + if (!(humanEntity instanceof Player)) { + throw new IllegalArgumentException("Smithing tables can only be opened by players"); + } + + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } + + getInventory().clear(); + + getInputComponent().display(getInventory(), 0); + getResultComponent().display(getInventory(), 3); + getPlayerInventoryComponent().display(); + + if (getPlayerInventoryComponent().hasItem()) { + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } + + getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); + } + + Inventory inventory = smithingTableInventory.openInventory((Player) humanEntity, getTitleHolder(), + getTopItems()); + + addInventory(inventory, this); + + this.viewers.add(humanEntity); + } + + @NotNull + @Contract(pure = true) + @Override + public ModernSmithingTableGui copy() { + ModernSmithingTableGui gui = new ModernSmithingTableGui(getTitleHolder(), super.plugin); + + gui.inputComponent = inputComponent.copy(); + gui.resultComponent = resultComponent.copy(); + gui.playerInventoryComponent = playerInventoryComponent.copy(); + + gui.setOnTopClick(this.onTopClick); + gui.setOnBottomClick(this.onBottomClick); + gui.setOnGlobalClick(this.onGlobalClick); + gui.setOnOutsideClick(this.onOutsideClick); + gui.setOnClose(this.onClose); + + return gui; + } + + @Override + public void click(@NotNull InventoryClickEvent event) { + int rawSlot = event.getRawSlot(); + + if (rawSlot >= 0 && rawSlot <= 2) { + getInputComponent().click(this, event, rawSlot); + } else if (rawSlot == 3) { + getResultComponent().click(this, event, 0); + } else { + getPlayerInventoryComponent().click(this, event, rawSlot - 4); + } + } + + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + + @Contract(pure = true) + @Override + public boolean isPlayerInventoryUsed() { + return getPlayerInventoryComponent().hasItem(); + } + + @NotNull + @Contract(pure = true) + @Override + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.SMITHING_NEW); + } + + /** + * Handles a human entity closing this gui. + * + * @param humanEntity the human entity closing the gui + * @since 0.10.9 + */ + public void handleClose(@NotNull HumanEntity humanEntity) { + this.viewers.remove(humanEntity); + } + + @Contract(pure = true) + @Override + public int getViewerCount() { + return this.viewers.size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(this.viewers); + } + + /** + * Gets the inventory component representing the input items + * + * @return the input component + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + public InventoryComponent getInputComponent() { + return inputComponent; + } + + /** + * Gets the inventory component representing the result + * + * @return the result component + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + public InventoryComponent getResultComponent() { + return resultComponent; + } + + /** + * Gets the inventory component representing the player inventory + * + * @return the player inventory component + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + public InventoryComponent getPlayerInventoryComponent() { + return playerInventoryComponent; + } + + /** + * Gets the top items + * + * @return the top items + * @since 0.10.9 + */ + @Nullable + @Contract(pure = true) + private ItemStack[] getTopItems() { + return new ItemStack[] { + getInputComponent().getItem(0, 0), + getInputComponent().getItem(1, 0), + getInputComponent().getItem(2, 0), + getResultComponent().getItem(0, 0) + }; + } + + /** + * Loads a smithing table gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui + * @return the loaded smithing table gui + * @see #load(Object, InputStream) + * @since 0.10.9 + */ + @Nullable + @Contract(pure = true) + public static ModernSmithingTableGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { + try { + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); + Element documentElement = document.getDocumentElement(); + + documentElement.normalize(); + + return load(instance, documentElement, plugin); + } catch (SAXException | ParserConfigurationException | IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Loads a smithing table gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui + * @return the loaded smithing table gui + * @since 0.10.9 + */ + @NotNull + public static ModernSmithingTableGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { + if (!element.hasAttribute("title")) { + throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); + } + + ModernSmithingTableGui smithingTableGui = new ModernSmithingTableGui(element.getAttribute("title"), plugin); + smithingTableGui.initializeOrThrow(instance, element); + + if (element.hasAttribute("populate")) { + return smithingTableGui; + } + + NodeList childNodes = element.getChildNodes(); + + for (int index = 0; index < childNodes.getLength(); index++) { + Node item = childNodes.item(index); + + if (item.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + Element componentElement = (Element) item; + + if (!componentElement.getTagName().equalsIgnoreCase("component")) { + throw new XMLLoadException("Gui element contains non-component tags"); + } + + if (!componentElement.hasAttribute("name")) { + throw new XMLLoadException("Component tag does not have a name specified"); + } + + InventoryComponent component; + + switch (componentElement.getAttribute("name")) { + case "input": + component = smithingTableGui.getInputComponent(); + break; + case "result": + component = smithingTableGui.getResultComponent(); + break; + case "player-inventory": + component = smithingTableGui.getPlayerInventoryComponent(); + break; + default: + throw new XMLLoadException("Unknown component name"); + } + + component.load(instance, componentElement, plugin); + } + + return smithingTableGui; + } + + /** + * Loads a smithing table gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded smithing table gui + * @since 0.10.9 + */ + @Nullable + @Contract(pure = true) + public static ModernSmithingTableGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(ModernSmithingTableGui.class)); + } + + /** + * Loads a smithing table gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded smithing table gui + * @since 0.10.9 + */ + @NotNull + public static ModernSmithingTableGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(ModernSmithingTableGui.class)); + } +} diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/ShulkerBoxGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/ShulkerBoxGui.java index f84ee3f6a..3bdb3f473 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/ShulkerBoxGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/ShulkerBoxGui.java @@ -1,16 +1,20 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.MergedGui; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; import com.github.stefvanschie.inventoryframework.pane.Pane; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -24,6 +28,7 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -33,7 +38,7 @@ * * @since 0.8.0 */ -public class ShulkerBoxGui extends NamedGui implements MergedGui { +public class ShulkerBoxGui extends NamedGui implements MergedGui, InventoryBased { /** * Represents the inventory component for the entire gui @@ -51,12 +56,48 @@ public ShulkerBoxGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public ShulkerBoxGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new shulker box gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #ShulkerBoxGui(String) + * @since 0.10.8 + */ + public ShulkerBoxGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new shulker box gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #ShulkerBoxGui(TextHolder) + * @since 0.10.8 + */ + public ShulkerBoxGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); - - getHumanEntityCache().store(humanEntity); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } + getInventory().clear(); int height = getInventoryComponent().getHeight(); getInventoryComponent().display(); @@ -65,14 +106,15 @@ public void show(@NotNull HumanEntity humanEntity) { InventoryComponent bottomComponent = getInventoryComponent().excludeRows(0, height - 5); topComponent.placeItems(getInventory(), 0); - bottomComponent.placeItems(humanEntity.getInventory(), 0); if (bottomComponent.hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } bottomComponent.placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -82,7 +124,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public ShulkerBoxGui copy() { - ShulkerBoxGui gui = new ShulkerBoxGui(getTitle()); + ShulkerBoxGui gui = new ShulkerBoxGui(getTitleHolder(), super.plugin); gui.inventoryComponent = inventoryComponent.copy(); @@ -95,6 +137,16 @@ public ShulkerBoxGui copy() { return gui; } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -128,8 +180,21 @@ public Collection getItems() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.SHULKER_BOX, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.SHULKER_BOX); + } + + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); } @NotNull @@ -144,19 +209,22 @@ public InventoryComponent getInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded shulker box gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static ShulkerBoxGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static ShulkerBoxGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -168,16 +236,18 @@ public static ShulkerBoxGui load(@NotNull Object instance, @NotNull InputStream * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded shulker box gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static ShulkerBoxGui load(@NotNull Object instance, @NotNull Element element) { + public static ShulkerBoxGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - ShulkerBoxGui shulkerBoxGui = new ShulkerBoxGui(element.getAttribute("title")); + ShulkerBoxGui shulkerBoxGui = new ShulkerBoxGui(element.getAttribute("title"), plugin); shulkerBoxGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -197,9 +267,9 @@ public static ShulkerBoxGui load(@NotNull Object instance, @NotNull Element elem InventoryComponent inventoryComponent = shulkerBoxGui.getInventoryComponent(); if (componentElement.getTagName().equalsIgnoreCase("component")) { - inventoryComponent.load(instance, componentElement); + inventoryComponent.load(instance, componentElement, plugin); } else { - inventoryComponent.load(instance, element); + inventoryComponent.load(instance, element, plugin); } break; @@ -207,4 +277,31 @@ public static ShulkerBoxGui load(@NotNull Object instance, @NotNull Element elem return shulkerBoxGui; } + + /** + * Loads a shulker box gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded shulker box gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static ShulkerBoxGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(ShulkerBoxGui.class)); + } + + /** + * Loads a shulker box gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded shulker box gui + * @since 0.8.0 + */ + @NotNull + public static ShulkerBoxGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(ShulkerBoxGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/SmithingTableGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/SmithingTableGui.java index bb058e527..48fdcd783 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/SmithingTableGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/SmithingTableGui.java @@ -1,18 +1,22 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; import com.github.stefvanschie.inventoryframework.util.version.Version; import com.github.stefvanschie.inventoryframework.util.version.VersionMatcher; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,13 +30,17 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** - * Represents a gui in the form of a smithing table + * Represents a gui in the form of a smithing table. This is the ;egacy variant with two input slots, available prior to + * Minecraft 1.20. * * @since 0.8.0 */ -public class SmithingTableGui extends NamedGui { +@Deprecated +public class SmithingTableGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the first item @@ -76,15 +84,52 @@ public SmithingTableGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public SmithingTableGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new smithing table gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #SmithingTableGui(String) + * @since 0.10.8 + */ + public SmithingTableGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new smithing table gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #SmithingTableGui(TextHolder) + * @since 0.10.8 + */ + public SmithingTableGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { if (!(humanEntity instanceof Player)) { throw new IllegalArgumentException("Smithing tables can only be opened by players"); } - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getFirstItemComponent().display(getInventory(), 0); getSecondItemComponent().display(getInventory(), 1); @@ -92,21 +137,26 @@ public void show(@NotNull HumanEntity humanEntity) { getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } - smithingTableInventory.openInventory((Player) humanEntity, getTitle(), getTopItems()); + //also let Bukkit know that we opened an inventory + humanEntity.openInventory(getInventory()); + + smithingTableInventory.openInventory((Player) humanEntity, getTitleHolder(), getTopItems()); } @NotNull @Contract(pure = true) @Override public SmithingTableGui copy() { - SmithingTableGui gui = new SmithingTableGui(getTitle()); + SmithingTableGui gui = new SmithingTableGui(getTitleHolder(), super.plugin); gui.firstItemComponent = firstItemComponent.copy(); gui.secondItemComponent = secondItemComponent.copy(); @@ -137,6 +187,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -146,8 +206,8 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.SMITHING, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.SMITHING); } /** @@ -161,7 +221,7 @@ public void handleClickEvent(@NotNull InventoryClickEvent event) { Player player = (Player) event.getWhoClicked(); if (slot >= 3 && slot <= 38) { - smithingTableInventory.sendItems(player, getTopItems()); + smithingTableInventory.sendItems(player, getTopItems(), event.getCurrentItem()); } else if (slot == 0 || slot == 1) { if (event.isCancelled()) { if (slot == 0) { @@ -185,6 +245,19 @@ public void handleClickEvent(@NotNull InventoryClickEvent event) { } } + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + /** * Gets the inventory component representing the first item * @@ -254,19 +327,22 @@ private ItemStack[] getTopItems() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded smithing table gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static SmithingTableGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static SmithingTableGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -278,16 +354,17 @@ public static SmithingTableGui load(@NotNull Object instance, @NotNull InputStre * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded smithing table gui - * @since 0.8.0 + * @since 0.10.8 */ @NotNull - public static SmithingTableGui load(@NotNull Object instance, @NotNull Element element) { + public static SmithingTableGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - SmithingTableGui smithingTableGui = new SmithingTableGui(element.getAttribute("title")); + SmithingTableGui smithingTableGui = new SmithingTableGui(element.getAttribute("title"), plugin); smithingTableGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -332,9 +409,36 @@ public static SmithingTableGui load(@NotNull Object instance, @NotNull Element e throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return smithingTableGui; } + + /** + * Loads a smithing table gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded smithing table gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static SmithingTableGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(SmithingTableGui.class)); + } + + /** + * Loads a smithing table gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded smithing table gui + * @since 0.8.0 + */ + @NotNull + public static SmithingTableGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(SmithingTableGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/SmokerGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/SmokerGui.java index c65fd42b1..f77ede998 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/SmokerGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/SmokerGui.java @@ -1,13 +1,17 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,13 +25,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a smoker * * @since 0.8.0 */ -public class SmokerGui extends NamedGui { +public class SmokerGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the ingredient @@ -63,11 +69,48 @@ public SmokerGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public SmokerGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new smoker gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #SmokerGui(String) + * @since 0.10.8 + */ + public SmokerGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new smoker gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #SmokerGui(TextHolder) + * @since 0.10.8 + */ + public SmokerGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getIngredientComponent().display(getInventory(), 0); getFuelComponent().display(getInventory(), 1); @@ -75,11 +118,13 @@ public void show(@NotNull HumanEntity humanEntity) { getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } humanEntity.openInventory(getInventory()); @@ -89,7 +134,7 @@ public void show(@NotNull HumanEntity humanEntity) { @Contract(pure = true) @Override public SmokerGui copy() { - SmokerGui gui = new SmokerGui(getTitle()); + SmokerGui gui = new SmokerGui(getTitleHolder(), super.plugin); gui.ingredientComponent = ingredientComponent.copy(); gui.fuelComponent = fuelComponent.copy(); @@ -120,6 +165,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -129,8 +184,21 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.SMOKER, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.SMOKER); + } + + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); } /** @@ -186,19 +254,21 @@ public InventoryComponent getPlayerInventoryComponent() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded smoker gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static SmokerGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static SmokerGui load(@NotNull Object instance, @NotNull InputStream inputStream, @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -210,16 +280,18 @@ public static SmokerGui load(@NotNull Object instance, @NotNull InputStream inpu * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded smoker gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static SmokerGui load(@NotNull Object instance, @NotNull Element element) { + public static SmokerGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - SmokerGui smokerGui = new SmokerGui(element.getAttribute("title")); + SmokerGui smokerGui = new SmokerGui(element.getAttribute("title"), plugin); smokerGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -264,9 +336,36 @@ public static SmokerGui load(@NotNull Object instance, @NotNull Element element) throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return smokerGui; } + + /** + * Loads a smoker gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded smoker gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static SmokerGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(SmokerGui.class)); + } + + /** + * Loads a smoker gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded smoker gui + * @since 0.8.0 + */ + @NotNull + public static SmokerGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(SmokerGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/StonecutterGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/StonecutterGui.java index 89d219181..80359c618 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/StonecutterGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/StonecutterGui.java @@ -1,18 +1,22 @@ package com.github.stefvanschie.inventoryframework.gui.type; +import com.github.stefvanschie.inventoryframework.HumanEntityCache; import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.InventoryBased; import com.github.stefvanschie.inventoryframework.gui.type.util.NamedGui; import com.github.stefvanschie.inventoryframework.util.version.Version; import com.github.stefvanschie.inventoryframework.util.version.VersionMatcher; -import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,13 +30,15 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * Represents a gui in the form of a stonecutter * * @since 0.8.0 */ -public class StonecutterGui extends NamedGui { +public class StonecutterGui extends NamedGui implements InventoryBased { /** * Represents the inventory component for the input @@ -70,36 +76,78 @@ public StonecutterGui(@NotNull String title) { super(title); } + /** + * Constructs a new GUI + * + * @param title the title/name of this gui. + * @since 0.10.0 + */ + public StonecutterGui(@NotNull TextHolder title) { + super(title); + } + + /** + * Constructs a new stonecutter gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #StonecutterGui(String) + * @since 0.10.8 + */ + public StonecutterGui(@NotNull String title, @NotNull Plugin plugin) { + super(title, plugin); + } + + /** + * Constructs a new stonecutter gui for the given {@code plugin}. + * + * @param title the title/name of this gui. + * @param plugin the owning plugin of this gui + * @see #StonecutterGui(TextHolder) + * @since 0.10.8 + */ + public StonecutterGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(title, plugin); + } + @Override public void show(@NotNull HumanEntity humanEntity) { if (!(humanEntity instanceof Player)) { throw new IllegalArgumentException("Enchanting tables can only be opened by players"); } - getInventory().clear(); + if (isDirty()) { + this.inventory = createInventory(); + markChanges(); + } - getHumanEntityCache().store(humanEntity); + getInventory().clear(); getInputComponent().display(getInventory(), 0); getResultComponent().display(getInventory(), 1); getPlayerInventoryComponent().display(); if (getPlayerInventoryComponent().hasItem()) { - humanEntity.getInventory().clear(); + HumanEntityCache humanEntityCache = getHumanEntityCache(); + + if (!humanEntityCache.contains(humanEntity)) { + humanEntityCache.storeAndClear(humanEntity); + } getPlayerInventoryComponent().placeItems(humanEntity.getInventory(), 0); - } else { - getHumanEntityCache().clearCache(humanEntity); } - stonecutterInventory.openInventory((Player) humanEntity, getTitle(), getTopItems()); + //also let Bukkit know that we opened an inventory + humanEntity.openInventory(getInventory()); + + stonecutterInventory.openInventory((Player) humanEntity, getTitleHolder(), getTopItems()); } @NotNull @Contract(pure = true) @Override public StonecutterGui copy() { - StonecutterGui gui = new StonecutterGui(getTitle()); + StonecutterGui gui = new StonecutterGui(getTitleHolder(), super.plugin); gui.inputComponent = inputComponent.copy(); gui.resultComponent = resultComponent.copy(); @@ -127,6 +175,16 @@ public void click(@NotNull InventoryClickEvent event) { } } + @NotNull + @Override + public Inventory getInventory() { + if (this.inventory == null) { + this.inventory = createInventory(); + } + + return inventory; + } + @Contract(pure = true) @Override public boolean isPlayerInventoryUsed() { @@ -136,8 +194,8 @@ public boolean isPlayerInventoryUsed() { @NotNull @Contract(pure = true) @Override - public Inventory createInventory(@NotNull String title) { - return Bukkit.createInventory(this, InventoryType.STONECUTTER, title); + public Inventory createInventory() { + return getTitleHolder().asInventoryTitle(this, InventoryType.STONECUTTER); } /** @@ -161,6 +219,19 @@ public void handleClickEvent(@NotNull InventoryClickEvent event) { } } + @Contract(pure = true) + @Override + public int getViewerCount() { + return getInventory().getViewers().size(); + } + + @NotNull + @Contract(pure = true) + @Override + public List getViewers() { + return new ArrayList<>(getInventory().getViewers()); + } + /** * Gets the inventory component representing the input * @@ -217,19 +288,22 @@ private ItemStack[] getTopItems() { * * @param instance the instance on which to reference fields and methods * @param inputStream the input stream containing the XML data + * @param plugin the plugin that will be the owner of the created gui * @return the loaded stone cutter gui - * @since 0.8.0 + * @see #load(Object, InputStream) + * @since 0.10.8 */ @Nullable @Contract(pure = true) - public static StonecutterGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + public static StonecutterGui load(@NotNull Object instance, @NotNull InputStream inputStream, + @NotNull Plugin plugin) { try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); Element documentElement = document.getDocumentElement(); documentElement.normalize(); - return load(instance, documentElement); + return load(instance, documentElement, plugin); } catch (SAXException | ParserConfigurationException | IOException e) { e.printStackTrace(); return null; @@ -241,16 +315,18 @@ public static StonecutterGui load(@NotNull Object instance, @NotNull InputStream * * @param instance the instance on which to reference fields and methods * @param element the element to load the gui from + * @param plugin the plugin that will be the owner of the created gui * @return the loaded stonecutter gui - * @since 0.8.0 + * @see #load(Object, Element) + * @since 0.10.8 */ @NotNull - public static StonecutterGui load(@NotNull Object instance, @NotNull Element element) { + public static StonecutterGui load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { if (!element.hasAttribute("title")) { throw new XMLLoadException("Provided XML element's gui tag doesn't have the mandatory title attribute set"); } - StonecutterGui stonecutterGui = new StonecutterGui(element.getAttribute("title")); + StonecutterGui stonecutterGui = new StonecutterGui(element.getAttribute("title"), plugin); stonecutterGui.initializeOrThrow(instance, element); if (element.hasAttribute("populate")) { @@ -292,9 +368,36 @@ public static StonecutterGui load(@NotNull Object instance, @NotNull Element ele throw new XMLLoadException("Unknown component name"); } - component.load(instance, componentElement); + component.load(instance, componentElement, plugin); } return stonecutterGui; } + + /** + * Loads a stone cutter gui from an XML file. + * + * @param instance the instance on which to reference fields and methods + * @param inputStream the input stream containing the XML data + * @return the loaded stone cutter gui + * @since 0.8.0 + */ + @Nullable + @Contract(pure = true) + public static StonecutterGui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(StonecutterGui.class)); + } + + /** + * Loads a stonecutter gui from the specified element, applying code references to the provided instance. + * + * @param instance the instance on which to reference fields and methods + * @param element the element to load the gui from + * @return the loaded stonecutter gui + * @since 0.8.0 + */ + @NotNull + public static StonecutterGui load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(StonecutterGui.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/util/Gui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/util/Gui.java index 028857291..d354f4b57 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/util/Gui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/util/Gui.java @@ -1,677 +1,805 @@ -package com.github.stefvanschie.inventoryframework.gui.type.util; - -import com.github.stefvanschie.inventoryframework.HumanEntityCache; -import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; -import com.github.stefvanschie.inventoryframework.gui.GuiListener; -import com.github.stefvanschie.inventoryframework.gui.type.*; -import com.github.stefvanschie.inventoryframework.pane.*; -import com.github.stefvanschie.inventoryframework.pane.component.*; -import com.github.stefvanschie.inventoryframework.util.XMLUtil; -import org.apache.commons.lang3.reflect.MethodUtils; -import org.bukkit.Bukkit; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.inventory.InventoryEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.java.JavaPlugin; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.util.*; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * The base class of all GUIs - */ -public abstract class Gui implements InventoryHolder { - - /** - * The inventory of this gui - */ - protected Inventory inventory; - - /** - * A player cache for storing player's inventories - */ - @NotNull - protected final HumanEntityCache humanEntityCache = new HumanEntityCache(); - - /** - * The consumer that will be called once a players clicks in the top-half of the gui - */ - @Nullable - protected Consumer onTopClick; - - /** - * The consumer that will be called once a players clicks in the bottom-half of the gui - */ - @Nullable - protected Consumer onBottomClick; - - /** - * The consumer that will be called once a players clicks in the gui or in their inventory - */ - @Nullable - protected Consumer onGlobalClick; - - /** - * The consumer that will be called once a player clicks outside of the gui screen - */ - @Nullable - protected Consumer onOutsideClick; - - /** - * The consumer that will be called once a player drags in the top-half of the gui - */ - @Nullable - protected Consumer onTopDrag; - - /** - * The consumer that will be called once a player drags in the bottom-half of the gui - */ - @Nullable - protected Consumer onBottomDrag; - - /** - * The consumer that will be called once a player drags in the gui or their inventory - */ - @Nullable - protected Consumer onGlobalDrag; - - /** - * The consumer that will be called once a player closes the gui - */ - @Nullable - protected Consumer onClose; - - /** - * Whether this gui is updating (as invoked by {@link #update()}), true if this is the case, false otherwise. This - * is used to indicate that inventory close events due to updating should be ignored. - */ - boolean updating = false; - - /** - * The pane mapping which will allow users to register their own panes to be used in XML files - */ - @NotNull - private static final Map> PANE_MAPPINGS = new HashMap<>(); - - /** - * The gui mappings which determine which gui type belongs to which identifier - */ - @NotNull - private static final Map> GUI_MAPPINGS - = new HashMap<>(); - - /** - * A map containing the relations between inventories and their respective gui. This is needed because Bukkit and - * Spigot ignore inventory holders for beacons, brewing stands, dispensers, droppers, furnaces and hoppers. The - * inventory holder for beacons is already being set properly via NMS, but this contains the other inventory types. - */ - @NotNull - private static final Map GUI_INVENTORIES = new WeakHashMap<>(); - - /** - * Whether listeners have ben registered by some gui - */ - private static boolean hasRegisteredListeners; - - /** - * Constructs a new GUI - * - * @since 0.8.0 - */ - public Gui() { - if (!hasRegisteredListeners) { - Bukkit.getPluginManager().registerEvents(new GuiListener(), - JavaPlugin.getProvidingPlugin(getClass())); - - hasRegisteredListeners = true; - } - } - - /** - * Creates a new inventory of the type of the implementing class with the provided title. - * - * @return the new inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - public abstract Inventory createInventory(); - - /** - * Shows a gui to a player - * - * @param humanEntity the human entity to show the gui to - */ - public abstract void show(@NotNull HumanEntity humanEntity); - - /** - * Makes a copy of this gui and returns it. This makes a deep copy of the gui. This entails that the underlying - * panes will be copied as per their {@link Pane#copy} and miscellaneous data will be copied. The copy of this gui, - * will however have no viewers even if this gui currently has viewers. With this, cache data for viewers will also - * be non-existent for the copied gui. The returned gui will never be reference equal to the current gui. - * - * @return a copy of the gui - * @since 0.6.2 - */ - @NotNull - @Contract(pure = true) - public abstract Gui copy(); - - /** - * This should delegate the provided inventory click event to the right pane, which can then handle this click event - * further. This should not call any internal click handlers, since those will already have been activated. - * - * @param event the event to delegate - * @since 0.8.0 - */ - public abstract void click(@NotNull InventoryClickEvent event); - - /** - * Gets whether the player inventory is currently in use. This means whether the player inventory currently has an - * item in it. - * - * @return true if the player inventory is occupied, false otherwise - * @since 0.8.0 - */ - public abstract boolean isPlayerInventoryUsed(); - - /** - * Gets the count of {@link HumanEntity} instances that are currently viewing this GUI. - * - * @return the count of viewers - * @since 0.5.19 - */ - @Contract(pure = true) - public int getViewerCount() { - return getInventory().getViewers().size(); - } - - /** - * Gets a mutable snapshot of the current {@link HumanEntity} viewers of this GUI. - * This is a snapshot (copy) and not a view, therefore modifications aren't visible. - * - * @return a snapshot of the current viewers - * @see #getViewerCount() - * @since 0.5.19 - */ - @NotNull - @Contract(pure = true) - public List getViewers() { - return new ArrayList<>(getInventory().getViewers()); - } - - /** - * Update the gui for everyone - */ - public void update() { - updating = true; - - getViewers().forEach(this::show); - - if (!updating) - throw new AssertionError("Gui#isUpdating became false before Gui#update finished"); - - updating = false; - } - - /** - * Adds the specified inventory and gui, so we can properly intercept clicks. - * - * @param inventory the inventory for the specified gui - * @param gui the gui belonging to the specified inventory - * @since 0.8.1 - */ - protected void addInventory(@NotNull Inventory inventory, @NotNull Gui gui) { - GUI_INVENTORIES.put(inventory, gui); - } - - /** - * Gets a gui from the specified inventory. Only guis of type beacon, brewing stand, dispenser, dropper, furnace and - * hopper can be retrieved. - * - * @param inventory the inventory to get the gui from - * @return the gui or null if the inventory doesn't have an accompanying gui - * @since 0.8.1 - */ - @Nullable - @Contract(pure = true) - public static Gui getGui(@NotNull Inventory inventory) { - return GUI_INVENTORIES.get(inventory); - } - - /** - * Gets the human entity cache used for this gui - * - * @return the human entity cache - * @see HumanEntityCache - * @since 0.5.4 - */ - @NotNull - @Contract(pure = true) - public HumanEntityCache getHumanEntityCache() { - return humanEntityCache; - } - - /** - * Loads a Gui from a given input stream. - * Returns null instead of throwing an exception in case of a failure. - * - * @param instance the class instance for all reflection lookups - * @param inputStream the file - * @return the gui or null if the loading failed - */ - @Nullable - public static Gui load(@NotNull Object instance, @NotNull InputStream inputStream) { - try { - Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); - Element documentElement = document.getDocumentElement(); - - documentElement.normalize(); - - if (!documentElement.hasAttribute("type")) { - throw new XMLLoadException("Type attribute must be specified when loading via Gui.load"); - } - - return GUI_MAPPINGS.get(documentElement.getAttribute("type")).apply(instance, documentElement); - } catch (SAXException | ParserConfigurationException | IOException e) { - e.printStackTrace(); - return null; - } - } - - /** - * Initializes standard fields from a Gui from a given input stream. - * Throws a {@link RuntimeException} instead of returning null in case of a failure. - * - * @param instance the class instance for all reflection lookups - * @param element the gui element - * @see #load(Object, InputStream) - */ - protected void initializeOrThrow(@NotNull Object instance, @NotNull Element element) { - if (element.hasAttribute("field")) - XMLUtil.loadFieldAttribute(instance, element, this); - - if (element.hasAttribute("onTopClick")) { - setOnTopClick(XMLUtil.loadOnEventAttribute(instance, - element, InventoryClickEvent.class, "onTopClick")); - } - - if (element.hasAttribute("onBottomClick")) { - setOnBottomClick(XMLUtil.loadOnEventAttribute(instance, - element, InventoryClickEvent.class, "onBottomClick")); - } - - if (element.hasAttribute("onGlobalClick")) { - setOnGlobalClick(XMLUtil.loadOnEventAttribute(instance, - element, InventoryClickEvent.class, "onGlobalClick")); - } - - if (element.hasAttribute("onOutsideClick")) { - setOnOutsideClick(XMLUtil.loadOnEventAttribute(instance, - element, InventoryClickEvent.class, "onOutsideClick")); - } - - if (element.hasAttribute("onTopDrag")) { - setOnTopDrag(XMLUtil.loadOnEventAttribute(instance, - element, InventoryDragEvent.class, "onTopDrag")); - } - - if (element.hasAttribute("onBottomDrag")) { - setOnBottomDrag(XMLUtil.loadOnEventAttribute(instance, - element, InventoryDragEvent.class, "onBottomDrag")); - } - - if (element.hasAttribute("onGlobalDrag")) { - setOnGlobalDrag(XMLUtil.loadOnEventAttribute(instance, - element, InventoryDragEvent.class, "onGlobalDrag")); - } - - if (element.hasAttribute("onClose")) { - setOnClose(XMLUtil.loadOnEventAttribute(instance, - element, InventoryCloseEvent.class, "onClose")); - } - - if (element.hasAttribute("populate")) { - try { - MethodUtils.invokeExactMethod(instance, "populate", this, Gui.class); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - Plugin plugin = JavaPlugin.getProvidingPlugin(Gui.class); - - throw new XMLLoadException("Error loading " + plugin.getName() + "'s gui with associated class: " - + instance.getClass().getSimpleName(), e); - } - } - } - - /** - * Set the consumer that should be called whenever this gui is clicked in. - * - * @param onTopClick the consumer that gets called - */ - public void setOnTopClick(@Nullable Consumer onTopClick) { - this.onTopClick = onTopClick; - } - - /** - * Calls the consumer (if it's not null) that was specified using {@link #setOnTopClick(Consumer)}, - * so the consumer that should be called whenever this gui is clicked in. - * Catches and logs all exceptions the consumer might throw. - * - * @param event the event to handle - * @since 0.6.0 - */ - public void callOnTopClick(@NotNull InventoryClickEvent event) { - callCallback(onTopClick, event, "onTopClick"); - } - - /** - * Set the consumer that should be called whenever the inventory is clicked in. - * - * @param onBottomClick the consumer that gets called - */ - public void setOnBottomClick(@Nullable Consumer onBottomClick) { - this.onBottomClick = onBottomClick; - } - - /** - * Calls the consumer (if it's not null) that was specified using {@link #setOnBottomClick(Consumer)}, - * so the consumer that should be called whenever the inventory is clicked in. - * Catches and logs all exceptions the consumer might throw. - * - * @param event the event to handle - * @since 0.6.0 - */ - public void callOnBottomClick(@NotNull InventoryClickEvent event) { - callCallback(onBottomClick, event, "onBottomClick"); - } - - /** - * Set the consumer that should be called whenever this gui or inventory is clicked in. - * - * @param onGlobalClick the consumer that gets called - */ - public void setOnGlobalClick(@Nullable Consumer onGlobalClick) { - this.onGlobalClick = onGlobalClick; - } - - /** - * Calls the consumer (if it's not null) that was specified using {@link #setOnGlobalClick(Consumer)}, - * so the consumer that should be called whenever this gui or inventory is clicked in. - * Catches and logs all exceptions the consumer might throw. - * - * @param event the event to handle - * @since 0.6.0 - */ - public void callOnGlobalClick(@NotNull InventoryClickEvent event) { - callCallback(onGlobalClick, event, "onGlobalClick"); - } - - /** - * Set the consumer that should be called whenever a player clicks outside the gui. - * - * @param onOutsideClick the consumer that gets called - * @since 0.5.7 - */ - public void setOnOutsideClick(@Nullable Consumer onOutsideClick) { - this.onOutsideClick = onOutsideClick; - } - - /** - * Calls the consumer (if it's not null) that was specified using {@link #setOnOutsideClick(Consumer)}, - * so the consumer that should be called whenever a player clicks outside the gui. - * Catches and logs all exceptions the consumer might throw. - * - * @param event the event to handle - * @since 0.6.0 - */ - public void callOnOutsideClick(@NotNull InventoryClickEvent event) { - callCallback(onOutsideClick, event, "onOutsideClick"); - } - - /** - * Set the consumer that should be called whenever this gui's top half is dragged in. - * - * @param onTopDrag the consumer that gets called - * @since 0.9.0 - */ - public void setOnTopDrag(@Nullable Consumer onTopDrag) { - this.onTopDrag = onTopDrag; - } - - /** - * Calls the consumer (if it's not null) that was specified using {@link #setOnTopDrag(Consumer)}, - * so the consumer that should be called whenever this gui's top half is dragged in. - * Catches and logs all exceptions the consumer might throw. - * - * @param event the event to handle - * @since 0.9.0 - */ - public void callOnTopDrag(@NotNull InventoryDragEvent event) { - callCallback(onTopDrag, event, "onTopDrag"); - } - - /** - * Set the consumer that should be called whenever the inventory is dragged in. - * - * @param onBottomDrag the consumer that gets called - * @since 0.9.0 - */ - public void setOnBottomDrag(@Nullable Consumer onBottomDrag) { - this.onBottomDrag = onBottomDrag; - } - - /** - * Calls the consumer (if it's not null) that was specified using {@link #setOnBottomDrag(Consumer)}, - * so the consumer that should be called whenever the inventory is dragged in. - * Catches and logs all exceptions the consumer might throw. - * - * @param event the event to handle - * @since 0.9.0 - */ - public void callOnBottomDrag(@NotNull InventoryDragEvent event) { - callCallback(onBottomDrag, event, "onBottomDrag"); - } - - /** - * Set the consumer that should be called whenever this gui or inventory is dragged in. - * - * @param onGlobalDrag the consumer that gets called - * @since 0.9.0 - */ - public void setOnGlobalDrag(@Nullable Consumer onGlobalDrag) { - this.onGlobalDrag = onGlobalDrag; - } - - /** - * Calls the consumer (if it's not null) that was specified using {@link #setOnGlobalDrag(Consumer)}, - * so the consumer that should be called whenever this gui or inventory is dragged in. - * Catches and logs all exceptions the consumer might throw. - * - * @param event the event to handle - * @since 0.6.0 - */ - public void callOnGlobalDrag(@NotNull InventoryDragEvent event) { - callCallback(onGlobalDrag, event, "onGlobalDrag"); - } - - /** - * Set the consumer that should be called whenever this gui is closed. - * - * @param onClose the consumer that gets called - */ - public void setOnClose(@Nullable Consumer onClose) { - this.onClose = onClose; - } - - /** - * Calls the consumer (if it's not null) that was specified using {@link #setOnClose(Consumer)}, - * so the consumer that should be called whenever this gui is closed. - * Catches and logs all exceptions the consumer might throw. - * - * @param event the event to handle - * @since 0.6.0 - */ - public void callOnClose(@NotNull InventoryCloseEvent event) { - callCallback(onClose, event, "onClose"); - } - - /** - * Calls the specified consumer (if it's not null) with the specified parameter, - * catching and logging all exceptions it might throw. - * - * @param callback the consumer to call if it isn't null - * @param event the value the consumer should accept - * @param callbackName the name of the action, used for logging - * @param the type of the value the consumer is accepting - */ - private void callCallback(@Nullable Consumer callback, - @NotNull T event, @NotNull String callbackName) { - if (callback == null) { - return; - } - - try { - callback.accept(event); - } catch (Throwable t) { - Logger logger = JavaPlugin.getProvidingPlugin(getClass()).getLogger(); - String message = "Exception while handling " + callbackName; - if (event instanceof InventoryClickEvent) { - InventoryClickEvent clickEvent = (InventoryClickEvent) event; - message += ", slot=" + clickEvent.getSlot(); - } - logger.log(Level.SEVERE, message, t); - } - } - - @NotNull - @Override - public Inventory getInventory() { - if (this.inventory == null) { - this.inventory = createInventory(); - } - - return inventory; - } - - /** - * Gets whether this gui is being updated, as invoked by {@link #update()}. This returns true if this is the case - * and false otherwise. - * - * @return whether this gui is being updated - * @since 0.5.15 - */ - @Contract(pure = true) - public boolean isUpdating() { - return updating; - } - - /** - * Registers a property that can be used inside an XML file to add additional new properties. - * - * @param attributeName the name of the property. This is the same name you'll be using to specify the property - * type in the XML file. - * @param function how the property should be processed. This converts the raw text input from the XML node value - * into the correct object type. - * @throws IllegalArgumentException when a property with this name is already registered. - */ - public static void registerProperty(@NotNull String attributeName, @NotNull Function function) { - Pane.registerProperty(attributeName, function); - } - - /** - * Registers a name that can be used inside an XML file to add custom panes - * - * @param name the name of the pane to be used in the XML file - * @param biFunction how the pane loading should be processed - * @throws IllegalArgumentException when a pane with this name is already registered - */ - public static void registerPane(@NotNull String name, @NotNull BiFunction biFunction) { - if (PANE_MAPPINGS.containsKey(name)) { - throw new IllegalArgumentException("pane name '" + name + "' is already registered"); - } - - PANE_MAPPINGS.put(name, biFunction); - } - - /** - * Registers a type that can be used inside an XML file to specify the gui type - * - * @param name the name of the type of gui to be used in an XML file - * @param biFunction how the gui creation should be processed - * @throws IllegalArgumentException when a gui type with this name is already registered - */ - public static void registerGui(@NotNull String name, @NotNull BiFunction biFunction) { - if (GUI_MAPPINGS.containsKey(name)) { - throw new IllegalArgumentException("Gui name '" + name + "' is already registered"); - } - - GUI_MAPPINGS.put(name, biFunction); - } - - /** - * Loads a pane by the given instance and node - * - * @param instance the instance - * @param node the node - * @return the pane - */ - @NotNull - public static Pane loadPane(@NotNull Object instance, @NotNull Node node) { - return PANE_MAPPINGS.get(node.getNodeName()).apply(instance, (Element) node); - } - - static { - registerPane("masonrypane", MasonryPane::load); - registerPane("outlinepane", OutlinePane::load); - registerPane("paginatedpane", PaginatedPane::load); - registerPane("patternpane", PatternPane::load); - registerPane("staticpane", StaticPane::load); - - registerPane("cyclebutton", CycleButton::load); - registerPane("label", Label::load); - registerPane("percentagebar", PercentageBar::load); - registerPane("slider", Slider::load); - registerPane("togglebutton", ToggleButton::load); - - registerGui("anvil", AnvilGui::load); - registerGui("barrel", BarrelGui::load); - registerGui("beacon", BeaconGui::load); - registerGui("blast-furnace", BlastFurnaceGui::load); - registerGui("brewing-stand", BrewingStandGui::load); - registerGui("cartography-table", CartographyTableGui::load); - registerGui("chest", ChestGui::load); - registerGui("crafting-table", CraftingTableGui::load); - registerGui("dispenser", DispenserGui::load); - registerGui("dropper", DropperGui::load); - registerGui("enchanting-table", EnchantingTableGui::load); - registerGui("ender-chest", EnderChestGui::load); - registerGui("furnace", FurnaceGui::load); - registerGui("grindstone", GrindstoneGui::load); - registerGui("hopper", HopperGui::load); - registerGui("shulker-box", ShulkerBoxGui::load); - registerGui("smithing-table", SmithingTableGui::load); - registerGui("smoker", SmokerGui::load); - registerGui("stonecutter", StonecutterGui::load); - } -} +package com.github.stefvanschie.inventoryframework.gui.type.util; + +import com.github.stefvanschie.inventoryframework.HumanEntityCache; +import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; +import com.github.stefvanschie.inventoryframework.gui.GuiListener; +import com.github.stefvanschie.inventoryframework.gui.type.*; +import com.github.stefvanschie.inventoryframework.pane.*; +import com.github.stefvanschie.inventoryframework.pane.component.*; +import com.github.stefvanschie.inventoryframework.util.TriFunction; +import com.github.stefvanschie.inventoryframework.util.XMLUtil; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.logging.Level; + +/** + * The base class of all GUIs + */ +public abstract class Gui { + + /** + * The plugin that owns this gui + */ + @NotNull + protected final Plugin plugin; + + /** + * The inventory of this gui + */ + protected Inventory inventory; + + /** + * A player cache for storing player's inventories + */ + @NotNull + protected final HumanEntityCache humanEntityCache = new HumanEntityCache(); + + /** + * The consumer that will be called once a players clicks in the top-half of the gui + */ + @Nullable + protected Consumer onTopClick; + + /** + * The consumer that will be called once a players clicks in the bottom-half of the gui + */ + @Nullable + protected Consumer onBottomClick; + + /** + * The consumer that will be called once a players clicks in the gui or in their inventory + */ + @Nullable + protected Consumer onGlobalClick; + + /** + * The consumer that will be called once a player clicks outside of the gui screen + */ + @Nullable + protected Consumer onOutsideClick; + + /** + * The consumer that will be called once a player drags in the top-half of the gui + */ + @Nullable + protected Consumer onTopDrag; + + /** + * The consumer that will be called once a player drags in the bottom-half of the gui + */ + @Nullable + protected Consumer onBottomDrag; + + /** + * The consumer that will be called once a player drags in the gui or their inventory + */ + @Nullable + protected Consumer onGlobalDrag; + + /** + * The consumer that will be called once a player closes the gui + */ + @Nullable + protected Consumer onClose; + + /** + * Whether this gui is updating (as invoked by {@link #update()}), true if this is the case, false otherwise. This + * is used to indicate that inventory close events due to updating should be ignored. + */ + boolean updating = false; + + /** + * The parent gui. This gui will be navigated to once a player closes this gui. If this is null, the player will not + * be redirected to another gui once they close this gui. + */ + @Nullable + private Gui parent; + + /** + * The pane mapping which will allow users to register their own panes to be used in XML files + */ + @NotNull + private static final Map> + PANE_MAPPINGS = new HashMap<>(); + + /** + * The gui mappings which determine which gui type belongs to which identifier + */ + @NotNull + private static final Map> + GUI_MAPPINGS = new HashMap<>(); + + /** + * A map containing the relations between inventories and their respective gui. This is needed because Bukkit and + * Spigot ignore inventory holders for beacons, brewing stands, dispensers, droppers, furnaces and hoppers. The + * inventory holder for beacons is already being set properly via NMS, but this contains the other inventory types. + */ + @NotNull + private static final Map GUI_INVENTORIES = new WeakHashMap<>(); + + /** + * Whether listeners have ben registered by some gui + */ + private static boolean hasRegisteredListeners; + + /** + * Constructs a new gui with the provided plugin. + * + * @param plugin the plugin + * @since 0.10.8 + */ + public Gui(@NotNull Plugin plugin) { + this.plugin = plugin; + + if (!hasRegisteredListeners) { + Bukkit.getPluginManager().registerEvents(new GuiListener(plugin), plugin); + + hasRegisteredListeners = true; + } + } + + /** + * Shows a gui to a player + * + * @param humanEntity the human entity to show the gui to + */ + public abstract void show(@NotNull HumanEntity humanEntity); + + /** + * Makes a copy of this gui and returns it. This makes a deep copy of the gui. This entails that the underlying + * panes will be copied as per their {@link Pane#copy} and miscellaneous data will be copied. The copy of this gui, + * will however have no viewers even if this gui currently has viewers. With this, cache data for viewers will also + * be non-existent for the copied gui. The original owning plugin of the gui is preserved, but the plugin will not + * be deeply copied. The returned gui will never be reference equal to the current gui. + * + * @return a copy of the gui + * @since 0.6.2 + */ + @NotNull + @Contract(pure = true) + public abstract Gui copy(); + + /** + * This should delegate the provided inventory click event to the right pane, which can then handle this click event + * further. This should not call any internal click handlers, since those will already have been activated. + * + * @param event the event to delegate + * @since 0.8.0 + */ + public abstract void click(@NotNull InventoryClickEvent event); + + /** + * Gets whether the player inventory is currently in use. This means whether the player inventory currently has an + * item in it. + * + * @return true if the player inventory is occupied, false otherwise + * @since 0.8.0 + */ + public abstract boolean isPlayerInventoryUsed(); + + /** + * Gets the count of {@link HumanEntity} instances that are currently viewing this GUI. + * + * @return the count of viewers + * @since 0.5.19 + */ + @Contract(pure = true) + public abstract int getViewerCount(); + + /** + * Gets a mutable snapshot of the current {@link HumanEntity} viewers of this GUI. + * This is a snapshot (copy) and not a view, therefore modifications aren't visible. + * + * @return a snapshot of the current viewers + * @see #getViewerCount() + * @since 0.5.19 + */ + @NotNull + @Contract(pure = true) + public abstract List getViewers(); + + /** + * Update the gui for everyone + */ + public void update() { + updating = true; + + for (HumanEntity viewer : getViewers()) { + ItemStack cursor = viewer.getItemOnCursor(); + viewer.setItemOnCursor(new ItemStack(Material.AIR)); + + show(viewer); + + viewer.setItemOnCursor(cursor); + } + + if (!updating) + throw new AssertionError("Gui#isUpdating became false before Gui#update finished"); + + updating = false; + } + + /** + * Adds the specified inventory and gui, so we can properly intercept clicks. + * + * @param inventory the inventory for the specified gui + * @param gui the gui belonging to the specified inventory + * @since 0.8.1 + */ + protected void addInventory(@NotNull Inventory inventory, @NotNull Gui gui) { + GUI_INVENTORIES.put(inventory, gui); + } + + /** + * Gets a gui from the specified inventory. Only guis of type beacon, brewing stand, dispenser, dropper, furnace and + * hopper can be retrieved. + * + * @param inventory the inventory to get the gui from + * @return the gui or null if the inventory doesn't have an accompanying gui + * @since 0.8.1 + */ + @Nullable + @Contract(pure = true) + public static Gui getGui(@NotNull Inventory inventory) { + return GUI_INVENTORIES.get(inventory); + } + + /** + * Gets the human entity cache used for this gui + * + * @return the human entity cache + * @see HumanEntityCache + * @since 0.5.4 + */ + @NotNull + @Contract(pure = true) + public HumanEntityCache getHumanEntityCache() { + return humanEntityCache; + } + + /** + * Loads a Gui from a given input stream. + * + * @param instance the class instance for all reflection lookups + * @param inputStream the file + * @return the gui or null if the loading failed + * @throws XMLLoadException if loading could not finish successfully, due to e.g., a malformed file + * @see #load(Object, InputStream) + * @since 0.10.8 + */ + @Nullable + public static Gui load(@NotNull Object instance, @NotNull InputStream inputStream, @NotNull Plugin plugin) { + try { + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); + Element documentElement = document.getDocumentElement(); + + documentElement.normalize(); + + if (!documentElement.hasAttribute("type")) { + throw new XMLLoadException("Type attribute must be specified when loading via Gui.load"); + } + + String type = documentElement.getAttribute("type"); + TriFunction mapping = GUI_MAPPINGS + .get(type); + + if (mapping == null) { + throw new XMLLoadException("Type attribute '" + type + "' is invalid"); + } + + return mapping.apply(instance, documentElement, plugin); + } catch (SAXException | ParserConfigurationException | IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Loads a Gui from a given input stream. + * Returns null instead of throwing an exception in case of a failure. + * + * @param instance the class instance for all reflection lookups + * @param inputStream the file + * @return the gui or null if the loading failed + * @throws XMLLoadException if loading could not finish successfully, due to e.g., a malformed file + */ + @Nullable + public static Gui load(@NotNull Object instance, @NotNull InputStream inputStream) { + return load(instance, inputStream, JavaPlugin.getProvidingPlugin(Gui.class)); + } + + /** + * Initializes standard fields from a Gui from a given input stream. + * Throws a {@link RuntimeException} instead of returning null in case of a failure. + * + * @param instance the class instance for all reflection lookups + * @param element the gui element + * @see #load(Object, InputStream) + */ + protected void initializeOrThrow(@NotNull Object instance, @NotNull Element element) { + if (element.hasAttribute("field")) + XMLUtil.loadFieldAttribute(instance, element, this); + + if (element.hasAttribute("onTopClick")) { + setOnTopClick(XMLUtil.loadOnEventAttribute(instance, + element, InventoryClickEvent.class, "onTopClick")); + } + + if (element.hasAttribute("onBottomClick")) { + setOnBottomClick(XMLUtil.loadOnEventAttribute(instance, + element, InventoryClickEvent.class, "onBottomClick")); + } + + if (element.hasAttribute("onGlobalClick")) { + setOnGlobalClick(XMLUtil.loadOnEventAttribute(instance, + element, InventoryClickEvent.class, "onGlobalClick")); + } + + if (element.hasAttribute("onOutsideClick")) { + setOnOutsideClick(XMLUtil.loadOnEventAttribute(instance, + element, InventoryClickEvent.class, "onOutsideClick")); + } + + if (element.hasAttribute("onTopDrag")) { + setOnTopDrag(XMLUtil.loadOnEventAttribute(instance, + element, InventoryDragEvent.class, "onTopDrag")); + } + + if (element.hasAttribute("onBottomDrag")) { + setOnBottomDrag(XMLUtil.loadOnEventAttribute(instance, + element, InventoryDragEvent.class, "onBottomDrag")); + } + + if (element.hasAttribute("onGlobalDrag")) { + setOnGlobalDrag(XMLUtil.loadOnEventAttribute(instance, + element, InventoryDragEvent.class, "onGlobalDrag")); + } + + if (element.hasAttribute("onClose")) { + setOnClose(XMLUtil.loadOnEventAttribute(instance, + element, InventoryCloseEvent.class, "onClose")); + } + + if (element.hasAttribute("populate")) { + XMLUtil.invokeMethod(instance, element.getAttribute("populate"), this, Gui.class); + } + } + + /** + * Set the consumer that should be called whenever this gui is clicked in. + * + * @param onTopClick the consumer that gets called + */ + public void setOnTopClick(@Nullable Consumer onTopClick) { + this.onTopClick = onTopClick; + } + + /** + * Calls the consumer (if it's not null) that was specified using {@link #setOnTopClick(Consumer)}, + * so the consumer that should be called whenever this gui is clicked in. + * Catches and logs all exceptions the consumer might throw. + * + * @param event the event to handle + * @since 0.6.0 + */ + public void callOnTopClick(@NotNull InventoryClickEvent event) { + callCallback(onTopClick, event, "onTopClick"); + } + + /** + * Set the consumer that should be called whenever the inventory is clicked in. + * + * @param onBottomClick the consumer that gets called + */ + public void setOnBottomClick(@Nullable Consumer onBottomClick) { + this.onBottomClick = onBottomClick; + } + + /** + * Calls the consumer (if it's not null) that was specified using {@link #setOnBottomClick(Consumer)}, + * so the consumer that should be called whenever the inventory is clicked in. + * Catches and logs all exceptions the consumer might throw. + * + * @param event the event to handle + * @since 0.6.0 + */ + public void callOnBottomClick(@NotNull InventoryClickEvent event) { + callCallback(onBottomClick, event, "onBottomClick"); + } + + /** + * Set the consumer that should be called whenever this gui or inventory is clicked in. + * + * @param onGlobalClick the consumer that gets called + */ + public void setOnGlobalClick(@Nullable Consumer onGlobalClick) { + this.onGlobalClick = onGlobalClick; + } + + /** + * Calls the consumer (if it's not null) that was specified using {@link #setOnGlobalClick(Consumer)}, + * so the consumer that should be called whenever this gui or inventory is clicked in. + * Catches and logs all exceptions the consumer might throw. + * + * @param event the event to handle + * @since 0.6.0 + */ + public void callOnGlobalClick(@NotNull InventoryClickEvent event) { + callCallback(onGlobalClick, event, "onGlobalClick"); + } + + /** + * Set the consumer that should be called whenever a player clicks outside the gui. + * + * @param onOutsideClick the consumer that gets called + * @since 0.5.7 + */ + public void setOnOutsideClick(@Nullable Consumer onOutsideClick) { + this.onOutsideClick = onOutsideClick; + } + + /** + * Calls the consumer (if it's not null) that was specified using {@link #setOnOutsideClick(Consumer)}, + * so the consumer that should be called whenever a player clicks outside the gui. + * Catches and logs all exceptions the consumer might throw. + * + * @param event the event to handle + * @since 0.6.0 + */ + public void callOnOutsideClick(@NotNull InventoryClickEvent event) { + callCallback(onOutsideClick, event, "onOutsideClick"); + } + + /** + * Set the consumer that should be called whenever this gui's top half is dragged in. + * + * @param onTopDrag the consumer that gets called + * @since 0.9.0 + */ + public void setOnTopDrag(@Nullable Consumer onTopDrag) { + this.onTopDrag = onTopDrag; + } + + /** + * Calls the consumer (if it's not null) that was specified using {@link #setOnTopDrag(Consumer)}, + * so the consumer that should be called whenever this gui's top half is dragged in. + * Catches and logs all exceptions the consumer might throw. + * + * @param event the event to handle + * @since 0.9.0 + */ + public void callOnTopDrag(@NotNull InventoryDragEvent event) { + callCallback(onTopDrag, event, "onTopDrag"); + } + + /** + * Set the consumer that should be called whenever the inventory is dragged in. + * + * @param onBottomDrag the consumer that gets called + * @since 0.9.0 + */ + public void setOnBottomDrag(@Nullable Consumer onBottomDrag) { + this.onBottomDrag = onBottomDrag; + } + + /** + * Calls the consumer (if it's not null) that was specified using {@link #setOnBottomDrag(Consumer)}, + * so the consumer that should be called whenever the inventory is dragged in. + * Catches and logs all exceptions the consumer might throw. + * + * @param event the event to handle + * @since 0.9.0 + */ + public void callOnBottomDrag(@NotNull InventoryDragEvent event) { + callCallback(onBottomDrag, event, "onBottomDrag"); + } + + /** + * Set the consumer that should be called whenever this gui or inventory is dragged in. + * + * @param onGlobalDrag the consumer that gets called + * @since 0.9.0 + */ + public void setOnGlobalDrag(@Nullable Consumer onGlobalDrag) { + this.onGlobalDrag = onGlobalDrag; + } + + /** + * Calls the consumer (if it's not null) that was specified using {@link #setOnGlobalDrag(Consumer)}, + * so the consumer that should be called whenever this gui or inventory is dragged in. + * Catches and logs all exceptions the consumer might throw. + * + * @param event the event to handle + * @since 0.6.0 + */ + public void callOnGlobalDrag(@NotNull InventoryDragEvent event) { + callCallback(onGlobalDrag, event, "onGlobalDrag"); + } + + /** + * Set the consumer that should be called whenever this gui is closed. + * + * @param onClose the consumer that gets called + */ + public void setOnClose(@Nullable Consumer onClose) { + this.onClose = onClose; + } + + /** + * Calls the consumer (if it's not null) that was specified using {@link #setOnClose(Consumer)}, + * so the consumer that should be called whenever this gui is closed. + * Catches and logs all exceptions the consumer might throw. + * + * @param event the event to handle + * @since 0.6.0 + */ + public void callOnClose(@NotNull InventoryCloseEvent event) { + callCallback(onClose, event, "onClose"); + } + + /** + * Calls the specified consumer (if it's not null) with the specified parameter, + * catching and logging all exceptions it might throw. + * + * @param callback the consumer to call if it isn't null + * @param event the value the consumer should accept + * @param callbackName the name of the action, used for logging + * @param the type of the value the consumer is accepting + */ + protected void callCallback(@Nullable Consumer callback, + @NotNull T event, @NotNull String callbackName) { + if (callback == null) { + return; + } + + try { + callback.accept(event); + } catch (Throwable t) { + String message = "Exception while handling " + callbackName; + if (event instanceof InventoryClickEvent) { + InventoryClickEvent clickEvent = (InventoryClickEvent) event; + message += ", slot=" + clickEvent.getSlot(); + } + + this.plugin.getLogger().log(Level.SEVERE, message, t); + } + } + + /** + * The parent gui will be shown to the specified {@link HumanEntity}. If no parent gui is set, then this method will + * silently do nothing. + * + * @param humanEntity the human entity to redirect + * @since 0.10.14 + */ + public void navigateToParent(@NotNull HumanEntity humanEntity) { + if (this.parent == null) { + return; + } + + this.parent.show(humanEntity); + } + + /** + * Sets the parent gui to the provided gui. This is the gui that a player will be navigated to once they close this + * gui. The navigation will occur after the close event handler, set by {@link #setOnClose(Consumer)}, is called. If + * there was already a previous parent set, the provided gui will override the previous one. + * + * @param gui the new parent gui + * @since 0.10.14 + */ + public void setParent(@NotNull Gui gui) { + this.parent = gui; + } + + /** + * Gets whether this gui is being updated, as invoked by {@link #update()}. This returns true if this is the case + * and false otherwise. + * + * @return whether this gui is being updated + * @since 0.5.15 + */ + @Contract(pure = true) + public boolean isUpdating() { + return updating; + } + + /** + * Registers a property that can be used inside an XML file to add additional new properties. + * + * @param attributeName the name of the property. This is the same name you'll be using to specify the property + * type in the XML file. + * @param function how the property should be processed. This converts the raw text input from the XML node value + * into the correct object type. + * @throws IllegalArgumentException when a property with this name is already registered. + */ + public static void registerProperty(@NotNull String attributeName, @NotNull Function function) { + Pane.registerProperty(attributeName, function); + } + + /** + * Registers a name that can be used inside an XML file to add custom panes + * + * @param name the name of the pane to be used in the XML file + * @param triFunction how the pane loading should be processed + * @throws IllegalArgumentException when a pane with this name is already registered + * @see #registerPane(String, BiFunction) + * @since 0.10.8 + */ + public static void registerPane(@NotNull String name, + @NotNull TriFunction triFunction) { + if (PANE_MAPPINGS.containsKey(name)) { + throw new IllegalArgumentException("pane name '" + name + "' is already registered"); + } + + PANE_MAPPINGS.put(name, triFunction); + } + + /** + * Registers a name that can be used inside an XML file to add custom panes + * + * @param name the name of the pane to be used in the XML file + * @param biFunction how the pane loading should be processed + * @throws IllegalArgumentException when a pane with this name is already registered + */ + public static void registerPane(@NotNull String name, @NotNull BiFunction biFunction) { + registerPane(name, (object, element, plugin) -> biFunction.apply(object, element)); + } + + /** + * Registers a type that can be used inside an XML file to specify the gui type + * + * @param name the name of the type of gui to be used in an XML file + * @param triFunction how the gui creation should be processed + * @throws IllegalArgumentException when a gui type with this name is already registered + * @since 0.10.8 + */ + public static void registerGui(@NotNull String name, + @NotNull TriFunction triFunction) { + if (GUI_MAPPINGS.containsKey(name)) { + throw new IllegalArgumentException("Gui name '" + name + "' is already registered"); + } + + GUI_MAPPINGS.put(name, triFunction); + } + + /** + * Registers a type that can be used inside an XML file to specify the gui type + * + * @param name the name of the type of gui to be used in an XML file + * @param biFunction how the gui creation should be processed + * @throws IllegalArgumentException when a gui type with this name is already registered + * @deprecated this method is no longer used internally and has been superseded by + * {@link #registerPane(String, TriFunction)} + */ + @Deprecated + public static void registerGui(@NotNull String name, + @NotNull BiFunction biFunction) { + registerGui(name, (object, element, plugin) -> biFunction.apply(object, element)); + } + + /** + * Loads a pane by the given instance and node + * + * @param instance the instance + * @param node the node + * @param plugin the plugin to load the pane with + * @return the pane + * @throws XMLLoadException if the name of the node does not correspond to a valid pane. + * @since 0.10.8 + */ + @NotNull + public static Pane loadPane(@NotNull Object instance, @NotNull Node node, @NotNull Plugin plugin) { + String name = node.getNodeName(); + TriFunction mapping = PANE_MAPPINGS.get(name); + + if (mapping == null) { + throw new XMLLoadException("Pane '" + name + "' is not registered or does not exist"); + } + + return mapping.apply(instance, (Element) node, plugin); + } + + /** + * Loads a pane by the given instance and node + * + * @param instance the instance + * @param node the node + * @return the pane + */ + @NotNull + public static Pane loadPane(@NotNull Object instance, @NotNull Node node) { + return loadPane(instance, node, JavaPlugin.getProvidingPlugin(Gui.class)); + } + + static { + registerPane("masonrypane", + (TriFunction) MasonryPane::load); + registerPane("outlinepane", + (TriFunction) OutlinePane::load); + registerPane("paginatedpane", + (TriFunction) PaginatedPane::load); + registerPane("patternpane", + (TriFunction) PatternPane::load); + registerPane("staticpane", + (TriFunction) StaticPane::load); + + registerPane("cyclebutton", + (TriFunction) CycleButton::load); + registerPane("label", + (TriFunction) Label::load); + registerPane("pagingbuttons", PagingButtons::load); + registerPane("percentagebar", + (TriFunction) PercentageBar::load); + registerPane("slider", + (TriFunction) Slider::load); + registerPane("togglebutton", + (TriFunction) ToggleButton::load); + + registerGui("anvil", + (TriFunction) AnvilGui::load); + registerGui("barrel", + (TriFunction) BarrelGui::load); + registerGui("beacon", + (TriFunction) BeaconGui::load); + registerGui("blast-furnace", + (TriFunction) BlastFurnaceGui::load); + registerGui("brewing-stand", + (TriFunction) BrewingStandGui::load); + registerGui("cartography-table", + (TriFunction) CartographyTableGui::load); + registerGui("chest", + (TriFunction) ChestGui::load); + registerGui("crafting-table", + (TriFunction) CraftingTableGui::load); + registerGui("dispenser", + (TriFunction) DispenserGui::load); + registerGui("dropper", + (TriFunction) DropperGui::load); + registerGui("enchanting-table", + (TriFunction) EnchantingTableGui::load); + registerGui("ender-chest", + (TriFunction) EnderChestGui::load); + registerGui("furnace", + (TriFunction) FurnaceGui::load); + registerGui("grindstone", + (TriFunction) GrindstoneGui::load); + registerGui("hopper", + (TriFunction) HopperGui::load); + registerGui("merchant", + (TriFunction) MerchantGui::load); + registerGui("shulker-box", + (TriFunction) ShulkerBoxGui::load); + registerGui("smithing-table", + (TriFunction) SmithingTableGui::load); + registerGui("smoker", + (TriFunction) SmokerGui::load); + registerGui("stonecutter", + (TriFunction) StonecutterGui::load); + } +} diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/util/InventoryBased.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/util/InventoryBased.java new file mode 100644 index 000000000..8aaa21c29 --- /dev/null +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/util/InventoryBased.java @@ -0,0 +1,20 @@ +package com.github.stefvanschie.inventoryframework.gui.type.util; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +public interface InventoryBased extends InventoryHolder { + + /** + * Creates a new inventory of the type of the implementing class. + * + * @return the new inventory + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + Inventory createInventory(); + +} diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/util/NamedGui.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/util/NamedGui.java index 1ac7127f5..2bba5b869 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/util/NamedGui.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/type/util/NamedGui.java @@ -1,21 +1,24 @@ package com.github.stefvanschie.inventoryframework.gui.type.util; -import org.bukkit.Bukkit; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.Inventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import java.util.List; - public abstract class NamedGui extends Gui { /** * The title of this gui */ @NotNull - private String title; + private TextHolder title; + + /** + * Whether the title is dirty i.e., has changed + */ + private boolean dirty = false; /** * Constructs a new gui with a title @@ -24,60 +27,67 @@ public abstract class NamedGui extends Gui { * @since 0.8.0 */ public NamedGui(@NotNull String title) { - this.title = title; + this(StringHolder.of(title)); } /** - * Creates a new inventory of the type of the implementing class with the provided title. + * Constructs a new gui with a title * - * @param title the title for the new inventory - * @return the new inventory - * @since 0.8.0 + * @param title the title/name of this gui + * @since 0.10.0 */ - @NotNull - @Contract(pure = true) - public abstract Inventory createInventory(@NotNull String title); - - @NotNull - @Override - public Inventory getInventory() { - if (this.inventory == null) { - this.inventory = createInventory(getTitle()); - } + public NamedGui(@NotNull TextHolder title) { + this(title, JavaPlugin.getProvidingPlugin(NamedGui.class)); + } - return inventory; + /** + * Constructs a new gui with a title for the given {@code plugin}. + * + * @param title the title/name of this gui + * @param plugin the owning plugin of this gui + * @see #NamedGui(String) + * @since 0.10.8 + */ + public NamedGui(@NotNull String title, @NotNull Plugin plugin) { + this(StringHolder.of(title), plugin); } - @NotNull - @Override - public Inventory createInventory() { - return createInventory(title); + /** + * Constructs a new gui with a title for the given {@code plugin}. + * + * @param title the title/name of this gui + * @param plugin the owning plugin of this gui + * @see #NamedGui(TextHolder) + * @since 0.10.8 + */ + public NamedGui(@NotNull TextHolder title, @NotNull Plugin plugin) { + super(plugin); + + this.title = title; } /** - * Sets the title for this inventory. This will (unlike most other methods) directly update itself in order - * to ensure all viewers will still be viewing the new inventory as well. + * Sets the title for this inventory. * * @param title the title */ public void setTitle(@NotNull String title) { - //copy the viewers - List viewers = getViewers(); + setTitle(StringHolder.of(title)); + } - this.inventory = createInventory(title); + /** + * Sets the title for this inventory. + * + * @param title the title + * @since 0.10.0 + */ + public void setTitle(@NotNull TextHolder title) { this.title = title; - - updating = true; - - for (HumanEntity viewer : viewers) { - show(viewer); - } - - updating = false; + this.dirty = true; } /** - * Returns the title of this gui + * Returns the title of this gui as a legacy string. * * @return the title * @since 0.8.0 @@ -85,6 +95,39 @@ public void setTitle(@NotNull String title) { @NotNull @Contract(pure = true) public String getTitle() { + return title.asLegacyString(); + } + + /** + * Returns the title of this GUI in a wrapped form. + * + * @return the title + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public TextHolder getTitleHolder() { return title; } + + /** + * Gets whether this title is dirty or not i.e. whether the title has changed. + * + * @return whether the title is dirty + * @since 0.10.0 + */ + @Contract(pure = true) + public boolean isDirty() { + return dirty; + } + + /** + * Marks that the changes present here have been accepted. This sets dirty to false. If dirty was already false, + * this will do nothing. + * + * @since 0.10.0 + */ + public void markChanges() { + this.dirty = false; + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/MasonryPane.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/MasonryPane.java index 2b09ca057..894e87daf 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/MasonryPane.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/MasonryPane.java @@ -4,7 +4,10 @@ import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.w3c.dom.Element; @@ -37,10 +40,35 @@ public class MasonryPane extends Pane implements Orientable { @NotNull private Orientation orientation = Orientation.HORIZONTAL; + /** + * Creates a new masonry pane + * + * @param slot the slot of the pane + * @param length the length of the pane + * @param height the height of the pane + * @param priority the priority of the pane + * @since 0.10.8 + */ + public MasonryPane(@NotNull Slot slot, int length, int height, @NotNull Priority priority) { + super(slot, length, height, priority); + } + public MasonryPane(int x, int y, int length, int height, @NotNull Priority priority) { super(x, y, length, height, priority); } + /** + * Creates a new masonry pane + * + * @param slot the slot of the pane + * @param length the length of the pane + * @param height the height of the pane + * @since 0.10.8 + */ + public MasonryPane(@NotNull Slot slot, int length, int height) { + super(slot, length, height); + } + public MasonryPane(int x, int y, int length, int height) { super(x, y, length, height); } @@ -64,6 +92,10 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs for (int paneIndex = 0; paneIndex < panes.size(); paneIndex++) { Pane pane = panes.get(paneIndex); + if (!pane.isVisible()) { + continue; + } + if (orientation == Orientation.HORIZONTAL) { outerLoop: for (int y = 0; y < height; y++) { @@ -93,8 +125,8 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs pane.display( inventoryComponent, - paneOffsetX + getX(), - paneOffsetY + getY(), + paneOffsetX + getSlot().getX(length), + paneOffsetY + getSlot().getY(length), Math.min(this.length, maxLength), Math.min(this.height, maxHeight) ); @@ -131,8 +163,8 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs pane.display( inventoryComponent, - paneOffsetX + getX(), - paneOffsetY + getY(), + paneOffsetX + getSlot().getX(length), + paneOffsetY + getSlot().getY(length), Math.min(this.length, maxLength), Math.min(this.height, maxHeight) ); @@ -151,10 +183,17 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp int length = Math.min(this.length, maxLength); int height = Math.min(this.height, maxHeight); - int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY); + Slot paneSlot = getSlot(); + + int xPosition = paneSlot.getX(maxLength); + int yPosition = paneSlot.getY(maxLength); + + int totalLength = inventoryComponent.getLength(); + + int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY); - int x = adjustedSlot % inventoryComponent.getLength(); - int y = adjustedSlot / inventoryComponent.getLength(); + int x = adjustedSlot % totalLength; + int y = adjustedSlot / totalLength; if (x < 0 || x >= length || y < 0 || y >= height) { return false; @@ -164,9 +203,13 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp boolean success = false; - for (Pane pane : panes) { - success = success || pane.click(gui, inventoryComponent, event, slot, paneOffsetX + getX(), - paneOffsetY + getY(), length, height); + for (Pane pane : new ArrayList<>(panes)) { + if (!pane.isVisible()) { + continue; + } + + success = success || pane.click(gui, inventoryComponent, event, slot, paneOffsetX + xPosition, + paneOffsetY + yPosition, length, height); } return success; @@ -176,7 +219,7 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp @Contract(pure = true) @Override public MasonryPane copy() { - MasonryPane masonryPane = new MasonryPane(x, y, length, height, getPriority()); + MasonryPane masonryPane = new MasonryPane(getSlot(), length, height, getPriority()); for (Pane pane : panes) { masonryPane.addPane(pane.copy()); @@ -241,10 +284,12 @@ public void setOrientation(@NotNull Orientation orientation) { * * @param instance the instance class * @param element the element + * @param plugin the plugin that will be the owner of the created items * @return the masonry pane + * @since 0.10.8 */ @NotNull - public static MasonryPane load(@NotNull Object instance, @NotNull Element element) { + public static MasonryPane load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { try { MasonryPane masonryPane = new MasonryPane( Integer.parseInt(element.getAttribute("length")), @@ -267,7 +312,7 @@ public static MasonryPane load(@NotNull Object instance, @NotNull Element elemen continue; } - masonryPane.addPane(Gui.loadPane(instance, pane)); + masonryPane.addPane(Gui.loadPane(instance, pane, plugin)); } return masonryPane; @@ -275,4 +320,19 @@ public static MasonryPane load(@NotNull Object instance, @NotNull Element elemen throw new XMLLoadException(exception); } } + + /** + * Loads a masonry pane from a given element + * + * @param instance the instance class + * @param element the element + * @return the masonry pane + * @deprecated this method is no longer used internally and has been superseded by + * {@link #load(Object, Element, Plugin)} + */ + @NotNull + @Deprecated + public static MasonryPane load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(MasonryPane.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/OutlinePane.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/OutlinePane.java index fdda3e95d..90fd53f71 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/OutlinePane.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/OutlinePane.java @@ -5,10 +5,13 @@ import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.pane.util.Mask; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; import com.github.stefvanschie.inventoryframework.util.GeometryUtil; import org.bukkit.Material; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.w3c.dom.Element; @@ -54,14 +57,29 @@ public class OutlinePane extends Pane implements Flippable, Orientable, Rotatabl */ private boolean flipHorizontally, flipVertically; + /** + * The alignment of this pane + */ + @NotNull + private Alignment alignment = Alignment.BEGIN; + /** * The mask for this pane */ @NotNull private Mask mask; - public OutlinePane(int x, int y, int length, int height, @NotNull Priority priority) { - super(x, y, length, height, priority); + /** + * Creates a new outline pane + * + * @param slot the slot of the pane + * @param length the length of the pane + * @param height the height of the pane + * @param priority the priority of the pane + * @since 0.10.8 + */ + public OutlinePane(@NotNull Slot slot, int length, int height, @NotNull Priority priority) { + super(slot, length, height, priority); this.items = new ArrayList<>(length * height); this.orientation = Orientation.HORIZONTAL; @@ -78,6 +96,23 @@ public OutlinePane(int x, int y, int length, int height, @NotNull Priority prior this.mask = new Mask(mask); } + public OutlinePane(int x, int y, int length, int height, @NotNull Priority priority) { + this(Slot.fromXY(x, y), length, height, priority); + } + + + /** + * Creates a new outline pane + * + * @param slot the slot of the pane + * @param length the length of the pane + * @param height the height of the pane + * @since 0.10.8 + */ + public OutlinePane(@NotNull Slot slot, int length, int height) { + this(slot, length, height, Priority.NORMAL); + } + public OutlinePane(int x, int y, int length, int height) { this(x, y, length, height, Priority.NORMAL); } @@ -92,98 +127,123 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs int length = Math.min(this.length, maxLength); int height = Math.min(this.height, maxHeight); - int x = 0, y = 0; + int itemIndex = 0; + int gapCount = 0; - if (orientation == Orientation.HORIZONTAL) { - outerloop: - for (int rowIndex = 0; rowIndex < inventoryComponent.getHeight(); rowIndex++) { - boolean[] row = mask.getRow(rowIndex); + int size; - for (int columnIndex = 0; columnIndex < inventoryComponent.getLength(); columnIndex++) { - if (!row[columnIndex]) { - continue; - } + if (getOrientation() == Orientation.HORIZONTAL) { + size = height; + } else if (getOrientation() == Orientation.VERTICAL) { + size = length; + } else { + throw new IllegalStateException("Unknown orientation '" + getOrientation() + "'"); + } - x = columnIndex; - y = rowIndex; - break outerloop; - } + for (int vectorIndex = 0; vectorIndex < size && getItems().size() > itemIndex; vectorIndex++) { + boolean[] maskLine; + + if (getOrientation() == Orientation.HORIZONTAL) { + maskLine = mask.getRow(vectorIndex); + } else if (getOrientation() == Orientation.VERTICAL) { + maskLine = mask.getColumn(vectorIndex); + } else { + throw new IllegalStateException("Unknown orientation '" + getOrientation() + "'"); } - } else if (orientation == Orientation.VERTICAL) { - outerloop: - for (int columnIndex = 0; columnIndex < inventoryComponent.getLength(); columnIndex++) { - boolean[] column = mask.getColumn(columnIndex); - - for (int rowIndex = 0; rowIndex < inventoryComponent.getHeight(); rowIndex++) { - if (!column[rowIndex]) { - continue; - } - x = columnIndex; - y = rowIndex; - break outerloop; + int enabled = 0; + + for (boolean bool : maskLine) { + if (bool) { + enabled++; } } - } - int itemAmount = items.size(); + GuiItem[] items; - outerloop: - for (int i = 0; i < (doesRepeat() ? Math.max(mask.amountOfEnabledSlots(), inventoryComponent.getSize()) : itemAmount); i++) { - GuiItem item = items.get(i % itemAmount); + if (doesRepeat()) { + items = new GuiItem[enabled]; + } else { + int remainingPositions = gapCount + (getItems().size() - itemIndex - 1) * (getGap() + 1) + 1; - if (!item.isVisible()) - continue; + items = new GuiItem[Math.min(enabled, remainingPositions)]; + } - int newX = x, newY = y; + for (int index = 0; index < items.length; index++) { + if (gapCount == 0) { + items[index] = getItems().get(itemIndex); - if (flipHorizontally) - newX = length - x - 1; + itemIndex++; - if (flipVertically) - newY = height - y - 1; + if (doesRepeat() && itemIndex >= getItems().size()) { + itemIndex = 0; + } - Map.Entry coordinates = GeometryUtil.processClockwiseRotation(newX, newY, length, height, - rotation); + gapCount = getGap(); + } else { + items[index] = null; - newX = coordinates.getKey(); - newY = coordinates.getValue(); + gapCount--; + } + } - if (newX >= 0 && newX < length && newY >= 0 && newY < height) { - int finalRow = getY() + newY + paneOffsetY; - int finalColumn = getX() + newX + paneOffsetX; + int index; - inventoryComponent.setItem(item, finalColumn, finalRow); + if (getAlignment() == Alignment.BEGIN) { + index = 0; + } else if (getAlignment() == Alignment.CENTER) { + index = -((enabled - items.length) / 2); + } else { + throw new IllegalStateException("Unknown alignment '" + getAlignment() + "'"); } - int gapCount = gap; + for (int opposingVectorIndex = 0; opposingVectorIndex < maskLine.length; opposingVectorIndex++) { + if (!maskLine[opposingVectorIndex]) { + continue; + } - do { - if (orientation == Orientation.HORIZONTAL) { - x++; + if (index >= 0 && index < items.length && items[index] != null) { + int x, y; + + if (getOrientation() == Orientation.HORIZONTAL) { + x = opposingVectorIndex; + y = vectorIndex; + } else if (getOrientation() == Orientation.VERTICAL) { + x = vectorIndex; + y = opposingVectorIndex; + } else { + throw new IllegalStateException("Unknown orientation '" + getOrientation() + "'"); + } - if (x >= length) { - y++; - x = 0; + if (flipHorizontally) { + x = length - x - 1; } - } else if (orientation == Orientation.VERTICAL) { - y++; - if (y >= height) { - x++; - y = 0; + if (flipVertically) { + y = height - y - 1; } - } - //stop the loop when there is no more space in the pane - if (x >= length || y >= height) { - break outerloop; - } + Map.Entry coordinates = GeometryUtil.processClockwiseRotation(x, y, + length, height, rotation); - if (mask.isEnabled(x, y)) { - gapCount--; + x = coordinates.getKey(); + y = coordinates.getValue(); + + if (x >= 0 && x < length && y >= 0 && y < height) { + Slot slot = getSlot(); + + int finalRow = slot.getY(maxLength) + y + paneOffsetY; + int finalColumn = slot.getX(maxLength) + x + paneOffsetX; + + GuiItem item = items[index]; + if (item.isVisible()) { + inventoryComponent.setItem(item, finalColumn, finalRow); + } + } } - } while (gapCount >= 0); + + index++; + } } } @@ -194,10 +254,17 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp int length = Math.min(this.length, maxLength); int height = Math.min(this.height, maxHeight); - int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY); + Slot paneSlot = getSlot(); + + int xPosition = paneSlot.getX(maxLength); + int yPosition = paneSlot.getY(maxLength); + + int totalLength = inventoryComponent.getLength(); - int x = adjustedSlot % inventoryComponent.getLength(); - int y = adjustedSlot / inventoryComponent.getLength(); + int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY); + + int x = adjustedSlot % totalLength; + int y = adjustedSlot / totalLength; //this isn't our item if (x < 0 || x >= length || y < 0 || y >= height) { @@ -227,7 +294,7 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp @Contract(pure = true) @Override public OutlinePane copy() { - OutlinePane outlinePane = new OutlinePane(x, y, length, height, getPriority()); + OutlinePane outlinePane = new OutlinePane(getSlot(), length, height, getPriority()); for (GuiItem item : items) { outlinePane.addItem(item.copy()); @@ -245,6 +312,7 @@ public OutlinePane copy() { outlinePane.flipHorizontally = flipHorizontally; outlinePane.flipVertically = flipVertically; outlinePane.mask = mask; + outlinePane.alignment = alignment; return outlinePane; } @@ -326,6 +394,16 @@ public void setHeight(int height) { applyMask(getMask().setHeight(height)); } + /** + * Aligns the pane in the way specified by the provided alignment. + * + * @param alignment the new alignment + * @since 0.10.1 + */ + public void align(@NotNull Alignment alignment) { + this.alignment = alignment; + } + @Override public void flipHorizontally(boolean flipHorizontally) { this.flipHorizontally = flipHorizontally; @@ -366,6 +444,18 @@ public Collection getPanes() { return new HashSet<>(); } + /** + * Gets the alignment set on this pane. + * + * @return the alignment + * @since 0.10.1 + */ + @NotNull + @Contract(pure = true) + public Alignment getAlignment() { + return this.alignment; + } + /** * Gets whether this outline pane repeats itself * @@ -439,10 +529,12 @@ public boolean isFlippedVertically() { * * @param instance the instance class * @param element the element + * @param plugin the plugin that will be the owner of the created items * @return the outline pane + * @since 0.10.8 */ @NotNull - public static OutlinePane load(@NotNull Object instance, @NotNull Element element) { + public static OutlinePane load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { try { OutlinePane outlinePane = new OutlinePane( Integer.parseInt(element.getAttribute("length")), @@ -455,6 +547,10 @@ public static OutlinePane load(@NotNull Object instance, @NotNull Element elemen if (element.hasAttribute("repeat")) outlinePane.setRepeat(Boolean.parseBoolean(element.getAttribute("repeat"))); + if (element.hasAttribute("alignment")) { + outlinePane.align(Alignment.valueOf(element.getAttribute("alignment").toUpperCase())); + } + Pane.load(outlinePane, instance, element); Flippable.load(outlinePane, element); Orientable.load(outlinePane, element); @@ -472,9 +568,9 @@ public static OutlinePane load(@NotNull Object instance, @NotNull Element elemen continue; if (item.getNodeName().equals("empty")) - outlinePane.addItem(new GuiItem(new ItemStack(Material.AIR))); + outlinePane.addItem(new GuiItem(new ItemStack(Material.AIR), plugin)); else - outlinePane.addItem(Pane.loadItem(instance, (Element) item)); + outlinePane.addItem(Pane.loadItem(instance, (Element) item, plugin)); } return outlinePane; @@ -482,4 +578,42 @@ public static OutlinePane load(@NotNull Object instance, @NotNull Element elemen throw new XMLLoadException(exception); } } -} \ No newline at end of file + + /** + * Loads an outline pane from a given element + * + * @param instance the instance class + * @param element the element + * @return the outline pane + * @deprecated this method is no longer used internally and has been superseded by + * {@link #load(Object, Element, Plugin)} + */ + @NotNull + @Deprecated + public static OutlinePane load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(OutlinePane.class)); + } + + /** + * An enum containing different alignments that can be used on the outline pane. + * + * @since 0.10.1 + */ + public enum Alignment { + + /** + * Aligns the items at the start of the pane. + * + * @since 0.10.1 + */ + BEGIN, + + /** + * Aligns the items in the center of the pane. If there is no exact center, this will preference the left (for a + * horizontal orientation) or the top (for a vertical orientation). + * + * @since 0.10.1 + */ + CENTER + } +} diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPane.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPane.java index 2237e0a61..c3778e757 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPane.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPane.java @@ -4,11 +4,14 @@ import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,15 +31,40 @@ public class PaginatedPane extends Pane { * A set of panes for the different pages */ @NotNull - private final Map> panes = new HashMap<>(); + private Map> panes = new HashMap<>(); /** * The current page */ private int page; + /** + * Creates a new paginated pane + * + * @param slot the slot of the pane + * @param length the length of the pane + * @param height the height of the pane + * @param priority the priority of the pane + * @since 0.10.8 + */ + public PaginatedPane(@NotNull Slot slot, int length, int height, @NotNull Priority priority) { + super(slot, length, height, priority); + } + public PaginatedPane(int x, int y, int length, int height, @NotNull Priority priority) { - super(x, y, length, height, priority); + this(Slot.fromXY(x, y), length, height, priority); + } + + /** + * Creates a new paginated pane + * + * @param slot the slot of the pane + * @param length the length of the pane + * @param height the height of the pane + * @since 0.10.8 + */ + public PaginatedPane(@NotNull Slot slot, int length, int height) { + this(slot, length, height, Priority.NORMAL); } public PaginatedPane(int x, int y, int length, int height) { @@ -64,6 +92,43 @@ public int getPage() { public int getPages() { return panes.size(); } + + /** + * Adds the specified pane to a new page. The new page will be at the index one after the highest indexed page + * currently in this paginated pane. If the highest index pane is {@code Integer.MAX_VALUE}, this method will throw + * an {@link ArithmeticException}. If this paginated pane has no pages, the index of the newly created page will + * be zero. + * + * @param pane the pane to add to a new page + * @since 0.10.8 + * @throws ArithmeticException if the highest indexed page is the maximum value + */ + public void addPage(@NotNull Pane pane) { + List list = new ArrayList<>(1); + + list.add(pane); + + if (this.panes.isEmpty()) { + this.panes.put(0, list); + + return; + } + + int highest = Integer.MIN_VALUE; + + for (int page : this.panes.keySet()) { + if (page > highest) { + highest = page; + } + } + + if (highest == Integer.MAX_VALUE) { + throw new ArithmeticException("Can't increment page index beyond its maximum value"); + } + + this.panes.put(highest + 1, list); + } + /** * Assigns a pane to a selected page * @@ -95,9 +160,11 @@ public void setPage(int page) { * This can be helpful when dealing with lists of unknown size. * * @param items The list to populate the pane with + * @param plugin the plugin that will be the owner of the items created + * @see #populateWithItemStacks(List) + * @since 0.10.8 */ - @Contract("null -> fail") - public void populateWithItemStacks(@NotNull List items) { + public void populateWithItemStacks(@NotNull List<@NotNull ItemStack> items, @NotNull Plugin plugin) { //Don't do anything if the list is empty if (items.isEmpty()) { return; @@ -117,13 +184,22 @@ public void populateWithItemStacks(@NotNull List items) { break; } - page.addItem(new GuiItem(items.get(index))); + page.addItem(new GuiItem(items.get(index), plugin)); } this.addPane(i, page); } } + /** + * Populates the PaginatedPane based on the provided list by adding new pages until all items can fit. + * This can be helpful when dealing with lists of unknown size. + * + * @param items The list to populate the pane with + */ + public void populateWithItemStacks(@NotNull List items) { + populateWithItemStacks(items, JavaPlugin.getProvidingPlugin(PaginatedPane.class)); + } /** * Populates the PaginatedPane based on the provided list by adding new pages until all items can fit. @@ -166,9 +242,12 @@ public void populateWithGuiItems(@NotNull List items) { * * @param displayNames The display names for all the items * @param material The material to use for the {@link org.bukkit.inventory.ItemStack}s + * @param plugin the plugin that will be the owner of the created items + * @see #populateWithNames(List, Material) + * @since 0.10.8 */ - @Contract("null, _ -> fail") - public void populateWithNames(@NotNull List displayNames, @Nullable Material material) { + public void populateWithNames(@NotNull List displayNames, @Nullable Material material, + @NotNull Plugin plugin) { if(material == null || material == Material.AIR) return; populateWithItemStacks(displayNames.stream().map(name -> { @@ -177,9 +256,21 @@ public void populateWithNames(@NotNull List displayNames, @Nullable Mate itemMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', name)); itemStack.setItemMeta(itemMeta); return itemStack; - }).collect(Collectors.toList())); + }).collect(Collectors.toList()), plugin); } + /** + * This method creates a list of ItemStacks all with the given {@code material} and the display names. + * After that it calls {@link #populateWithItemStacks(List)} + * This method also translates the color char {@code &} for all names. + * + * @param displayNames The display names for all the items + * @param material The material to use for the {@link org.bukkit.inventory.ItemStack}s + */ + public void populateWithNames(@NotNull List displayNames, @Nullable Material material) { + populateWithNames(displayNames, material, JavaPlugin.getProvidingPlugin(PaginatedPane.class)); + } + @Override public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength, int maxHeight) { @@ -190,8 +281,14 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs } for (Pane pane : panes) { - int newPaneOffsetX = paneOffsetX + getX(); - int newPaneOffsetY = paneOffsetY + getY(); + if (!pane.isVisible()) { + continue; + } + + Slot slot = getSlot(); + + int newPaneOffsetX = paneOffsetX + slot.getX(maxLength); + int newPaneOffsetY = paneOffsetY + slot.getY(maxLength); int newMaxLength = Math.min(length, maxLength); int newMaxHeight = Math.min(height, maxHeight); @@ -206,10 +303,17 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp int length = Math.min(this.length, maxLength); int height = Math.min(this.height, maxHeight); - int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY); + Slot paneSlot = getSlot(); + + int xPosition = paneSlot.getX(maxLength); + int yPosition = paneSlot.getY(maxLength); - int x = adjustedSlot % inventoryComponent.getLength(); - int y = adjustedSlot / inventoryComponent.getLength(); + int totalLength = inventoryComponent.getLength(); + + int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY); + + int x = adjustedSlot % totalLength; + int y = adjustedSlot / totalLength; //this isn't our item if (x < 0 || x >= length || y < 0 || y >= height) { @@ -220,9 +324,13 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp boolean success = false; - for (Pane pane : this.panes.getOrDefault(page, Collections.emptyList())) { - success = success || pane.click(gui, inventoryComponent, event, slot,paneOffsetX + getX(), - paneOffsetY + getY(), length, height); + for (Pane pane : new ArrayList<>(this.panes.getOrDefault(page, Collections.emptyList()))) { + if (!pane.isVisible()) { + continue; + } + + success = success || pane.click(gui, inventoryComponent, event, slot,paneOffsetX + xPosition, + paneOffsetY + yPosition, length, height); } return success; @@ -232,7 +340,7 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp @Contract(pure = true) @Override public PaginatedPane copy() { - PaginatedPane paginatedPane = new PaginatedPane(x, y, length, height, getPriority()); + PaginatedPane paginatedPane = new PaginatedPane(getSlot(), length, height, getPriority()); for (Map.Entry> entry : panes.entrySet()) { for (Pane pane : entry.getValue()) { @@ -250,6 +358,36 @@ public PaginatedPane copy() { return paginatedPane; } + /** + * Deletes a page and all its associated panes from this paginated pane. It also decrements the indexes of all pages + * beyond the specified page by one. For example, given a sequence of pages 0, 1, 2, 3, 4, upon removing page 2, the + * new sequence of pages will be 0, 1, 2, 3. If the specified page does not exist, then this method will silently do + * nothing. + * + * @param page the page to delete + * @since 0.10.5 + */ + public void deletePage(int page) { + if (this.panes.remove(page) == null) { + return; + } + + Map> newPanes = new HashMap<>(); + + for (Map.Entry> entry : this.panes.entrySet()) { + int index = entry.getKey(); + List panes = entry.getValue(); + + if (index > page) { + newPanes.put(index - 1, panes); + } else { + newPanes.put(index, panes); + } + } + + this.panes = newPanes; + } + @NotNull @Contract(pure = true) @Override @@ -306,10 +444,12 @@ public void clear() { * * @param instance the instance class * @param element the element + * @param plugin the plugin that will be used to create the items * @return the paginated pane + * @since 0.10.8 */ @NotNull - public static PaginatedPane load(@NotNull Object instance, @NotNull Element element) { + public static PaginatedPane load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { try { PaginatedPane paginatedPane = new PaginatedPane( Integer.parseInt(element.getAttribute("length")), @@ -321,15 +461,22 @@ public static PaginatedPane load(@NotNull Object instance, @NotNull Element elem if (element.hasAttribute("populate")) return paginatedPane; + if (element.hasAttribute("id")) { + element.setIdAttribute("id", true); + element.setUserData("pane", paginatedPane, null); + } + int pageCount = 0; NodeList childNodes = element.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node item = childNodes.item(i); - if (item.getNodeType() != Node.ELEMENT_NODE) continue; + if(!item.getNodeName().equals("page")) + throw new XMLLoadException("Panes have to be inside page tag"); + NodeList innerNodes = item.getChildNodes(); for (int j = 0; j < innerNodes.getLength(); j++) { @@ -339,7 +486,7 @@ public static PaginatedPane load(@NotNull Object instance, @NotNull Element elem continue; } - paginatedPane.addPane(pageCount, Gui.loadPane(instance, pane)); + paginatedPane.addPane(pageCount, Gui.loadPane(instance, pane, plugin)); } pageCount++; @@ -350,4 +497,19 @@ public static PaginatedPane load(@NotNull Object instance, @NotNull Element elem throw new XMLLoadException(exception); } } + + /** + * Loads a paginated pane from a given element + * + * @param instance the instance class + * @param element the element + * @return the paginated pane + * @deprecated this method is no longer used internally and has been superseded by + * {@link #load(Object, Element, Plugin)} + */ + @NotNull + @Deprecated + public static PaginatedPane load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(PaginatedPane.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Pane.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Pane.java index 79a7afed8..193753b02 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Pane.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Pane.java @@ -1,16 +1,18 @@ package com.github.stefvanschie.inventoryframework.pane; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.exception.XMLReflectionException; +import com.github.stefvanschie.inventoryframework.pane.util.Mask; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; +import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil; import com.github.stefvanschie.inventoryframework.util.SkullUtil; import com.github.stefvanschie.inventoryframework.util.UUIDTagType; import com.github.stefvanschie.inventoryframework.util.XMLUtil; import com.google.common.primitives.Primitives; -import org.apache.commons.lang3.reflect.MethodUtils; -import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.enchantments.Enchantment; @@ -18,6 +20,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -32,8 +35,6 @@ import java.util.*; import java.util.function.Consumer; import java.util.function.Function; -import java.util.logging.Level; -import java.util.logging.Logger; /** * The base class for all panes. @@ -43,8 +44,15 @@ public abstract class Pane { /** * The starting position of this pane, which is 0 by default */ + @Deprecated protected int x = 0, y = 0; + /** + * The position of this pane, which is (0,0) by default + */ + @NotNull + protected Slot slot = Slot.fromXY(0, 0); + /** * Length is horizontal, height is vertical */ @@ -81,19 +89,18 @@ public abstract class Pane { /** * Constructs a new default pane * - * @param x the upper left x coordinate of the pane - * @param y the upper left y coordinate of the pane + * @param slot the slot of the pane * @param length the length of the pane * @param height the height of the pane * @param priority the priority of the pane + * @since 0.10.8 */ - protected Pane(int x, int y, int length, int height, @NotNull Priority priority) { + protected Pane(@NotNull Slot slot, int length, int height, @NotNull Priority priority) { if (length == 0 || height == 0) { throw new IllegalArgumentException("Length and height of pane must be greater than zero"); } - this.x = x; - this.y = y; + setSlot(slot); this.length = length; this.height = height; @@ -104,6 +111,19 @@ protected Pane(int x, int y, int length, int height, @NotNull Priority priority) this.uuid = UUID.randomUUID(); } + /** + * Constructs a new default pane + * + * @param x the upper left x coordinate of the pane + * @param y the upper left y coordinate of the pane + * @param length the length of the pane + * @param height the height of the pane + * @param priority the priority of the pane + */ + protected Pane(int x, int y, int length, int height, @NotNull Priority priority) { + this(Slot.fromXY(x, y), length, height, priority); + } + /** * Constructs a new default pane, with no position * @@ -124,6 +144,17 @@ protected Pane(int length, int height) { this.uuid = UUID.randomUUID(); } + /** + * Constructs a new default pane + * + * @param slot the slot of the pane + * @param length the length of the pane + * @param height the height of the pane + */ + protected Pane(Slot slot, int length, int height) { + this(slot, length, height, Priority.NORMAL); + } + /** * Constructs a new default pane * @@ -167,6 +198,20 @@ public void setHeight(int height) { this.height = height; } + /** + * Sets the slot of this pane. + * + * @param slot the slot + * @since 0.10.8 + */ + public void setSlot(@NotNull Slot slot) { + this.slot = slot; + + //the length should be the length of the parent container, but we don't have that, so just use one + this.x = slot.getX(1); + this.y = slot.getY(1); + } + /** * Set the x coordinate of this pane * @@ -174,6 +219,8 @@ public void setHeight(int height) { */ public void setX(int x) { this.x = x; + + this.slot = Slot.fromXY(x, getY()); } /** @@ -183,6 +230,8 @@ public void setX(int x) { */ public void setY(int y) { this.y = y; + + this.slot = Slot.fromXY(getX(), y); } /** @@ -217,12 +266,27 @@ public UUID getUUID() { return uuid; } + /** + * Gets the slot of the position of this pane + * + * @return the slot + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + public Slot getSlot() { + return this.slot; + } + /** * Gets the x coordinate of this pane * * @return the x coordinate + * @deprecated when the slot was specified as an indexed position, this may return the wrong value; + * {@link #getSlot()} should be used instead */ @Contract(pure = true) + @Deprecated public int getX() { return x; } @@ -231,8 +295,11 @@ public int getX() { * Gets the y coordinate of this pane * * @return the y coordinate + * @deprecated when the slot was specified as an indexed position, this may return the wrong value; + * {@link #getSlot()} should be used instead */ @Contract(pure = true) + @Deprecated public int getY() { return y; } @@ -299,13 +366,21 @@ public void setPriority(@NotNull Priority priority) { * * @param instance the instance * @param element the element + * @param plugin the plugin that will be the owner of the created item * @return the gui item + * @see #loadItem(Object, Element) + * @since 0.10.8 */ @NotNull @Contract(pure = true) - public static GuiItem loadItem(@NotNull Object instance, @NotNull Element element) { + public static GuiItem loadItem(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { String id = element.getAttribute("id"); - Material material = Objects.requireNonNull(Material.matchMaterial(id.toUpperCase(Locale.getDefault()))); + Material material = Material.matchMaterial(id.toUpperCase(Locale.getDefault())); + + if (material == null) { + throw new XMLLoadException("Can't find material for '" + id + "'"); + } + boolean hasAmount = element.hasAttribute("amount"); boolean hasDamage = element.hasAttribute("damage"); int amount = hasAmount ? Integer.parseInt(element.getAttribute("amount")) : 1; @@ -358,12 +433,8 @@ public static GuiItem loadItem(@NotNull Object instance, @NotNull Element elemen if (!innerNode.getNodeName().equals("line")) continue; - boolean hasLore = itemMeta.hasLore(); - List lore = hasLore ? Objects.requireNonNull(itemMeta.getLore()) : new ArrayList<>(); - - lore.add(ChatColor.translateAlternateColorCodes('&', innerNode - .getTextContent())); - itemMeta.setLore(lore); + TextHolder.deserialize(innerNode.getTextContent()) + .asItemLoreAtEnd(itemMeta); itemStack.setItemMeta(itemMeta); break; case "enchantments": @@ -388,8 +459,14 @@ public static GuiItem loadItem(@NotNull Object instance, @NotNull Element elemen } else if (nodeName.equals("displayname")) { ItemMeta itemMeta = Objects.requireNonNull(itemStack.getItemMeta()); - itemMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', item - .getTextContent())); + TextHolder.deserialize(item.getTextContent()) + .asItemDisplayName(itemMeta); + + itemStack.setItemMeta(itemMeta); + } else if (nodeName.equals("modeldata")) { + ItemMeta itemMeta = Objects.requireNonNull(itemStack.getItemMeta()); + + itemMeta.setCustomModelData(Integer.parseInt(item.getTextContent())); itemStack.setItemMeta(itemMeta); } else if (nodeName.equals("skull") && itemStack.getItemMeta() instanceof SkullMeta) { @@ -446,8 +523,8 @@ else if (parameterCount == properties.size() + 1) { Object attribute = properties.get(i); if (!(parameterTypes[1 + i].isPrimitive() && - Primitives.unwrap(attribute.getClass()).isAssignableFrom(parameterTypes[1 + i])) && - !attribute.getClass().isAssignableFrom(parameterTypes[1 + i])) + parameterTypes[1 + i].isAssignableFrom(Primitives.unwrap(attribute.getClass()))) && + !parameterTypes[1 + i].isAssignableFrom(attribute.getClass())) correct = false; } @@ -475,17 +552,13 @@ else if (parameterCount == properties.size() + 1) { } } - GuiItem item = new GuiItem(itemStack, action); + GuiItem item = new GuiItem(itemStack, action, plugin); if (element.hasAttribute("field")) XMLUtil.loadFieldAttribute(instance, element, item); if (element.hasAttribute("populate")) { - try { - MethodUtils.invokeExactMethod(instance, "populate", item, GuiItem.class); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException exception) { - throw new XMLLoadException(exception); - } + XMLUtil.invokeMethod(instance, element.getAttribute("populate"), item, GuiItem.class); } item.setProperties(properties); @@ -493,17 +566,24 @@ else if (parameterCount == properties.size() + 1) { return item; } - public static void load(@NotNull Pane pane, @NotNull Object instance, @NotNull Element element) { - if (element.hasAttribute("x")) { - pane.setX(Integer.parseInt(element.getAttribute("x"))); - } + /** + * Loads an item from an instance and an element + * + * @param instance the instance + * @param element the element + * @return the gui item + */ + @NotNull + @Contract(pure = true) + public static GuiItem loadItem(@NotNull Object instance, @NotNull Element element) { + return loadItem(instance, element, JavaPlugin.getProvidingPlugin(Pane.class)); + } - if (element.hasAttribute("y")) { - pane.setY(Integer.parseInt(element.getAttribute("y"))); - } + public static void load(@NotNull Pane pane, @NotNull Object instance, @NotNull Element element) { + pane.setSlot(Slot.deserialize(element)); if (element.hasAttribute("priority")) - pane.setPriority(Priority.valueOf(element.getAttribute("priority"))); + pane.setPriority(Priority.valueOf(element.getAttribute("priority").toUpperCase())); if (element.hasAttribute("visible")) pane.setVisible(Boolean.parseBoolean(element.getAttribute("visible"))); @@ -530,6 +610,28 @@ public static void load(@NotNull Pane pane, @NotNull Object instance, @NotNull E } } + /** + * Checks whether a {@link GuiItem} is the same item as the given {@link ItemStack}. The item will be compared using + * internal data. When the item does not have this data, this method will return false. If the item does have such + * data, but its value does not match, false is also returned. This method will not mutate any of the provided + * arguments. + * + * @param guiItem the gui item to check + * @param item the item which the gui item should be checked against + * @return true if the {@link GuiItem} matches the {@link ItemStack}, false otherwise + * @since 0.10.14 + */ + @Contract(pure = true) + protected static boolean matchesItem(@NotNull GuiItem guiItem, @NotNull ItemStack item) { + ItemMeta meta = item.getItemMeta(); + + if (meta == null) { + return false; + } + + return guiItem.getUUID().equals(meta.getPersistentDataContainer().get(guiItem.getKey(), UUIDTagType.INSTANCE)); + } + /** * Finds a type of {@link GuiItem} from the provided collection of items based on the provided {@link ItemStack}. * The items will be compared using internal data. When the item does not have this data, this method will return @@ -548,19 +650,13 @@ public static void load(@NotNull Pane pane, @NotNull Object instance, @NotNull E @Nullable @Contract(pure = true) protected static T findMatchingItem(@NotNull Collection items, @NotNull ItemStack item) { - ItemMeta meta = item.getItemMeta(); - if (meta == null) { - return null; - } - - UUID uuid = meta.getPersistentDataContainer().get(GuiItem.KEY_UUID, UUIDTagType.INSTANCE); - if (uuid == null) { - return null; + for (T guiItem : items) { + if (matchesItem(guiItem, item)) { + return guiItem; + } } - return items.stream() - .filter(guiItem -> guiItem.getUUID().equals(uuid)) - .findAny().orElse(null); + return null; } /** @@ -615,7 +711,6 @@ public void setOnClick(@Nullable Consumer onClick) { /** * Calls the consumer (if it's not null) that was specified using {@link #setOnClick(Consumer)}, * so the consumer that should be called whenever this pane is clicked in. - * Catches and logs all exceptions the consumer might throw. * * @param event the event to handle * @since 0.6.0 @@ -624,17 +719,93 @@ protected void callOnClick(@NotNull InventoryClickEvent event) { if (onClick == null) { return; } - + + try { onClick.accept(event); } catch (Throwable t) { - Logger logger = JavaPlugin.getProvidingPlugin(getClass()).getLogger(); - logger.log(Level.SEVERE, "Exception while handling click event in inventory '" - + event.getView().getTitle() + "', slot=" + event.getSlot() + ", for " - + getClass().getSimpleName() + ", x=" + x + ", y=" + y + ", length=" + length + ", height=" + height, t); + throw new RuntimeException( + "Exception while handling click event in inventory '" + + InventoryViewUtil.getInstance().getTitle(event.getView()) + "', slot=" + event.getSlot() + + ", for " + getClass().getSimpleName() + ", x=" + getX() + ", y=" + getY() + + ", length=" + length + ", height=" + height, + t + ); } } + /** + * Creates a pane which displays as a border around the outside of the pane consisting of the provided item. The + * slot, length and height parameters are used for the respective properties of the pane. If either the length or + * height is negative an {@link IllegalArgumentException} will be thrown. + * + * @param slot the slot of the pane + * @param length the length of the pane + * @param height the height of the pane + * @param item the item of which the border is made + * @return the created pane which displays a border + * @since 0.10.8 + * @throws IllegalArgumentException if length or height is negative + */ + @NotNull + @Contract(pure = true) + public static Pane createBorder(Slot slot, int length, int height, @NotNull GuiItem item) { + if (length < 0) { + throw new IllegalArgumentException("Length should be non-negative"); + } + + if (height < 0) { + throw new IllegalArgumentException("Height should be non-negative"); + } + + String[] mask = new String[height]; + + if (height > 0) { + mask[0] = createLine(length); + } + + if (height > 1) { + mask[height - 1] = createLine(length); + } + + for (int yIndex = 1; yIndex < height - 1; yIndex++) { + StringBuilder builder = new StringBuilder("1"); + + for (int i = 0; i < length - 2; i++) { + builder.append('0'); + } + + mask[yIndex] = builder.append('1').toString(); + } + + OutlinePane pane = new OutlinePane(slot, length, height); + pane.applyMask(new Mask(mask)); + pane.addItem(item); + pane.setRepeat(true); + + return pane; + } + + /** + * Creates a pane which displays as a border around the outside of the pane consisting of the provided item. The x, + * y, length and height parameters are used for the respective properties of the pane. If either the length or + * height is negative an {@link IllegalArgumentException} will be thrown. + * + * @param x the x coordinate of the pane + * @param y the y coordinate of the pane + * @param length the length of the pane + * @param height the height of the pane + * @param item the item of which the border is made + * @return the created pane which displays a border + * @since 0.10.7 + * @throws IllegalArgumentException if length or height is negative + */ + @NotNull + @Contract(pure = true) + public static Pane createBorder(int x, int y, int length, int height, @NotNull GuiItem item) { + return createBorder(Slot.fromXY(x, y), length, height, item); + } + /** * Registers a property that can be used inside an XML file to add additional new properties. * The use of {@link Gui#registerProperty(String, Function)} is preferred over this method. @@ -653,6 +824,31 @@ public static void registerProperty(@NotNull String attributeName, @NotNull Func PROPERTY_MAPPINGS.put(attributeName, function); } + /** + * Creates a string containing the character '1' repeated length amount of times. If the provided length is negative + * an {@link IllegalArgumentException} will be thrown. + * + * @param length the length of the string + * @return the string containing '1's + * @since 0.10.7 + * @throws IllegalArgumentException if length is negative + */ + @NotNull + @Contract(pure = true) + private static String createLine(int length) { + if (length < 0) { + throw new IllegalArgumentException("Length should be non-negative"); + } + + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < length; i++) { + builder.append('1'); + } + + return builder.toString(); + } + /** * An enum representing the rendering priorities for the panes. Uses a similar system to Bukkit's * {@link org.bukkit.event.EventPriority} system diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PatternPane.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PatternPane.java index 4adde932e..86a02db3a 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PatternPane.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PatternPane.java @@ -5,9 +5,12 @@ import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; import com.github.stefvanschie.inventoryframework.pane.util.Pattern; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; import com.github.stefvanschie.inventoryframework.util.GeometryUtil; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.w3c.dom.Element; @@ -54,27 +57,42 @@ public class PatternPane extends Pane implements Flippable, Rotatable { /** * Constructs a new pattern pane. * - * @param x the upper left x coordinate of the pane - * @param y the upper left y coordinate of the pane + * @param slot the slot of the pane * @param length the length of the pane * @param height the height of the pane * @param priority the priority of the pane * @param pattern the pattern of the pane * @throws IllegalArgumentException when the pane and pattern dimensions don't match - * @since 0.9.8 + * @since 0.10.8 */ - public PatternPane(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Pattern pattern) { - super(x, y, length, height, priority); + public PatternPane(@NotNull Slot slot, int length, int height, @NotNull Priority priority, @NotNull Pattern pattern) { + super(slot, length, height, priority); if (pattern.getLength() != length || pattern.getHeight() != height) { throw new IllegalArgumentException( - "Dimensions of the provided pattern do not match the dimensions of the pane" + "Dimensions of the provided pattern do not match the dimensions of the pane" ); } this.pattern = pattern; } + /** + * Constructs a new pattern pane. + * + * @param x the upper left x coordinate of the pane + * @param y the upper left y coordinate of the pane + * @param length the length of the pane + * @param height the height of the pane + * @param priority the priority of the pane + * @param pattern the pattern of the pane + * @throws IllegalArgumentException when the pane and pattern dimensions don't match + * @since 0.9.8 + */ + public PatternPane(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Pattern pattern) { + this(Slot.fromXY(x, y), length, height, priority, pattern); + } + /** * Constructs a new pattern pane, with no position. * @@ -88,6 +106,20 @@ public PatternPane(int length, int height, @NotNull Pattern pattern) { this(0, 0, length, height, pattern); } + /** + * Constructs a new pattern pane. + * + * @param slot the slot of the pane + * @param length the length of the pane + * @param height the height of the pane + * @param pattern the pattern of the pane + * @throws IllegalArgumentException when the pane and pattern dimensions don't match + * @since 0.10.8 + */ + public PatternPane(@NotNull Slot slot, int length, int height, @NotNull Pattern pattern) { + this(slot, length, height, Priority.NORMAL, pattern); + } + /** * Constructs a new pattern pane. * @@ -133,8 +165,10 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs newX = coordinates.getKey(); newY = coordinates.getValue(); - int finalRow = getY() + newY + paneOffsetY; - int finalColumn = getX() + newX + paneOffsetX; + Slot slot = getSlot(); + + int finalRow = slot.getY(maxLength) + newY + paneOffsetY; + int finalColumn = slot.getX(maxLength) + newX + paneOffsetX; inventoryComponent.setItem(item, finalColumn, finalRow); } @@ -148,10 +182,17 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp int length = Math.min(this.length, maxLength); int height = Math.min(this.height, maxHeight); - int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY); + Slot paneSlot = getSlot(); + + int xPosition = paneSlot.getX(maxLength); + int yPosition = paneSlot.getY(maxLength); + + int totalLength = inventoryComponent.getLength(); + + int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY); - int x = adjustedSlot % inventoryComponent.getLength(); - int y = adjustedSlot / inventoryComponent.getLength(); + int x = adjustedSlot % totalLength; + int y = adjustedSlot / totalLength; //this isn't our item if (x < 0 || x >= length || y < 0 || y >= height) { @@ -181,16 +222,16 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp @Contract(pure = true) @Override public PatternPane copy() { - PatternPane patternPane = new PatternPane(getX(), getY(), getLength(), getHeight(), getPriority(), getPattern()); + PatternPane patternPane = new PatternPane(getSlot(), getLength(), getHeight(), getPriority(), getPattern()); patternPane.setVisible(isVisible()); patternPane.onClick = onClick; patternPane.uuid = uuid; - patternPane.setRotation(getRotation()); - patternPane.flipHorizontally(isFlippedHorizontally()); - patternPane.flipVertically(isFlippedVertically()); + patternPane.rotation = rotation; + patternPane.flippedHorizontally = flippedHorizontally; + patternPane.flippedVertically = flippedVertically; return patternPane; } @@ -339,11 +380,13 @@ public int getRotation() { * Loads a pattern pane from a given element * * @param instance the instance class - * @param element the element + * @param element the element + * @param plugin the plugin that will own the underlying items * @return the pattern pane + * @since 0.10.8 */ @NotNull - public static PatternPane load(@NotNull Object instance, @NotNull Element element) { + public static PatternPane load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { try { NodeList childNodes = element.getChildNodes(); @@ -387,7 +430,7 @@ public static PatternPane load(@NotNull Object instance, @NotNull Element elemen throw new XMLLoadException("Binding has multiple inner tags, one expected"); } - guiItem = Pane.loadItem(instance, (Element) guiItemNode); + guiItem = Pane.loadItem(instance, (Element) guiItemNode, plugin); } //guaranteed to only be a single code point @@ -422,4 +465,19 @@ public static PatternPane load(@NotNull Object instance, @NotNull Element elemen throw new XMLLoadException(exception); } } + + /** + * Loads a pattern pane from a given element + * + * @param instance the instance class + * @param element the element + * @return the pattern pane + * @deprecated this method is no longer used internally and has been superseded by + * {@link #load(Object, Element, Plugin)} + */ + @NotNull + @Deprecated + public static PatternPane load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(PatternPane.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/StaticPane.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/StaticPane.java index b77249f35..1661708a0 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/StaticPane.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/StaticPane.java @@ -4,9 +4,12 @@ import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; import com.github.stefvanschie.inventoryframework.util.GeometryUtil; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -19,6 +22,12 @@ /** * A pane for static items and stuff. All items will have to be specified a slot, or will be added in the next position. + *

+ * This pane allows you to specify the positions of the items either in the form of an x and y coordinate pair or as an + * index, in which case the indexing starts from the top left and continues to the right and bottom, with the horizontal + * axis taking priority. There are nuances at play with regard to mixing these two types of positioning systems within + * the same pane. It's recommended to only use one of these systems per pane and to not mix them. + *

*/ public class StaticPane extends Pane implements Flippable, Rotatable { @@ -27,7 +36,7 @@ public class StaticPane extends Pane implements Flippable, Rotatable { * the key and the y coordinate is the value. */ @NotNull - private final Map, GuiItem> items; + private final Map items; /** * The clockwise rotation of this pane in degrees @@ -39,12 +48,37 @@ public class StaticPane extends Pane implements Flippable, Rotatable { */ private boolean flipHorizontally, flipVertically; - public StaticPane(int x, int y, int length, int height, @NotNull Priority priority) { - super(x, y, length, height, priority); + /** + * Creates a new static pane. + * + * @param slot the slot of the pane + * @param length the length of the pane + * @param height the height of the pane + * @param priority the priority of the pane + * @since 0.10.8 + */ + public StaticPane(Slot slot, int length, int height, @NotNull Priority priority) { + super(slot, length, height, priority); this.items = new HashMap<>(length * height); } + public StaticPane(int x, int y, int length, int height, @NotNull Priority priority) { + this(Slot.fromXY(x, y), length, height, priority); + } + + /** + * Creates a new static pane. + * + * @param slot the slot of the pane + * @param length the length of the pane + * @param height the height of the pane + * @since 0.10.8 + */ + public StaticPane(Slot slot, int length, int height) { + this(slot, length, height, Priority.NORMAL); + } + public StaticPane(int x, int y, int length, int height) { this(x, y, length, height, Priority.NORMAL); } @@ -53,6 +87,18 @@ public StaticPane(int length, int height) { this(0, 0, length, height); } + /** + * {@inheritDoc} + * + * If there are multiple items in the same position when displaying the items, either one of those items may be + * shown. In particular, there is no guarantee that a specific item will be shown. + * + * @param inventoryComponent {@inheritDoc} + * @param paneOffsetX {@inheritDoc} + * @param paneOffsetY {@inheritDoc} + * @param maxLength {@inheritDoc} + * @param maxHeight {@inheritDoc} + */ @Override public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength, int maxHeight) { @@ -60,9 +106,10 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs int height = Math.min(this.height, maxHeight); items.entrySet().stream().filter(entry -> entry.getValue().isVisible()).forEach(entry -> { - Map.Entry location = entry.getKey(); + Slot location = entry.getKey(); - int x = location.getKey(), y = location.getValue(); + int x = location.getX(getLength()); + int y = location.getY(getLength()); if (flipHorizontally) x = length - x - 1; @@ -82,27 +129,41 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs GuiItem item = entry.getValue(); - int finalRow = getY() + y + paneOffsetY; - int finalColumn = getX() + x + paneOffsetX; + Slot slot = getSlot(); + int finalRow = slot.getY(maxLength) + y + paneOffsetY; + int finalColumn = slot.getX(maxLength) + x + paneOffsetX; inventoryComponent.setItem(item, finalColumn, finalRow); }); } /** - * Adds a gui item at the specific spot in the pane. If the coordinates as specified by the x and y parameters is - * already occupied, that item will be replaced by the item parameter. + * Adds a gui item at the specific spot in the pane. If there is another item specified in terms of x and y + * coordinates that are equal to the coordinates of this item, the old item will be overwritten by this item. * * @param item the item to set * @param x the x coordinate of the position of the item * @param y the y coordinate of the position of the item */ public void addItem(@NotNull GuiItem item, int x, int y) { - items.keySet().removeIf(entry -> entry.getKey() == x && entry.getValue() == y); - - items.put(new AbstractMap.SimpleEntry<>(x, y), item); + addItem(item, Slot.fromXY(x, y)); } + /** + * Adds a gui item at the specific spot in the pane. If the slot is specified in terms of an x and y coordinate pair + * and this pane contains another item whose position is specified as such and these positions are equal, the old + * item will be overwritten by this item. If the slot is specified in terms of an index and this pane contains + * another item whose position is specified as such and these positions are equal, the old item will be overwritten + * by this item. + * + * @param item the item to set + * @param slot the position of the item + * @since 0.10.8 + */ + public void addItem(@NotNull GuiItem item, Slot slot) { + this.items.put(slot, item); + } + /** * Removes the specified item from the pane * @@ -113,6 +174,29 @@ public void removeItem(@NotNull GuiItem item) { items.values().removeIf(guiItem -> guiItem.equals(item)); } + /** + * Removes the specified item from the pane. This will only remove items whose slot was specified in terms of an x + * and y coordinate pair which matches the coordinate specified. + * + * @param x the x coordinate of the item to remove + * @param y the y coordinate of the item to remove + * @since 0.10.0 + */ + public void removeItem(int x, int y) { + this.items.remove(Slot.fromXY(x, y)); + } + + /** + * Removes the specified item from the pane. This will only remove items whose slot was specified in the same way as + * the original slot and whose slot positions match. + * + * @param slot the slot of the item to remove + * @since 0.10.8 + */ + public void removeItem(@NotNull Slot slot) { + this.items.remove(slot); + } + @Override public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent, @NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength, @@ -120,10 +204,17 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp int length = Math.min(this.length, maxLength); int height = Math.min(this.height, maxHeight); - int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY); + Slot paneSlot = getSlot(); + + int xPosition = paneSlot.getX(maxLength); + int yPosition = paneSlot.getY(maxLength); + + int totalLength = inventoryComponent.getLength(); - int x = adjustedSlot % inventoryComponent.getLength(); - int y = adjustedSlot / inventoryComponent.getLength(); + int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY); + + int x = adjustedSlot % totalLength; + int y = adjustedSlot / totalLength; //this isn't our item if (x < 0 || x >= length || y < 0 || y >= height) { @@ -153,12 +244,10 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp @Contract(pure = true) @Override public StaticPane copy() { - StaticPane staticPane = new StaticPane(x, y, length, height, getPriority()); - - for (Map.Entry, GuiItem> entry : items.entrySet()) { - Map.Entry coordinates = entry.getKey(); + StaticPane staticPane = new StaticPane(getSlot(), length, height, getPriority()); - staticPane.addItem(entry.getValue().copy(), coordinates.getKey(), coordinates.getValue()); + for (Map.Entry entry : items.entrySet()) { + staticPane.addItem(entry.getValue().copy(), entry.getKey()); } staticPane.setVisible(isVisible()); @@ -189,31 +278,45 @@ public void setRotation(int rotation) { * Fills all empty space in the pane with the given {@code itemStack} and adds the given action * * @param itemStack The {@link ItemStack} to fill the empty space with - * @param action The action called whenever an interaction with the item happens - * @since 0.5.9 + * @param action The action called whenever an interaction with the item happens + * @param plugin the plugin that will be the owner of the created items + * @see #fillWith(ItemStack, Consumer) + * @since 0.10.8 */ - public void fillWith(@NotNull ItemStack itemStack, @Nullable Consumer action) { + public void fillWith(@NotNull ItemStack itemStack, @Nullable Consumer action, + @NotNull Plugin plugin) { //The non empty spots - Set> locations = this.items.keySet(); + Set locations = this.items.keySet(); for (int y = 0; y < this.getHeight(); y++) { for (int x = 0; x < this.getLength(); x++) { boolean found = false; - for (Map.Entry location : locations) { - if (location.getKey() == x && location.getValue() == y) { + for (Slot location : locations) { + if (location.getX(getLength()) == x && location.getY(getLength()) == y) { found = true; break; } } if (!found) { - this.addItem(new GuiItem(itemStack, action), x, y); + this.addItem(new GuiItem(itemStack, action, plugin), x, y); } } } } + /** + * Fills all empty space in the pane with the given {@code itemStack} and adds the given action + * + * @param itemStack The {@link ItemStack} to fill the empty space with + * @param action The action called whenever an interaction with the item happens + * @since 0.5.9 + */ + public void fillWith(@NotNull ItemStack itemStack, @Nullable Consumer action) { + fillWith(itemStack, action, JavaPlugin.getProvidingPlugin(StaticPane.class)); + } + /** * Fills all empty space in the pane with the given {@code itemStack} * @@ -275,11 +378,13 @@ public boolean isFlippedVertically() { * Loads an outline pane from a given element * * @param instance the instance class - * @param element the element + * @param element the element + * @param plugin the plugin that will be the owner of the udnerlying items * @return the outline pane + * @since 0.10.8 */ @NotNull - public static StaticPane load(@NotNull Object instance, @NotNull Element element) { + public static StaticPane load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { try { StaticPane staticPane = new StaticPane( Integer.parseInt(element.getAttribute("length")), @@ -303,8 +408,7 @@ public static StaticPane load(@NotNull Object instance, @NotNull Element element Element child = (Element) item; - staticPane.addItem(Pane.loadItem(instance, child), Integer.parseInt(child.getAttribute("x")), - Integer.parseInt(child.getAttribute("y"))); + staticPane.addItem(Pane.loadItem(instance, child, plugin), Slot.deserialize(child)); } return staticPane; @@ -312,4 +416,19 @@ public static StaticPane load(@NotNull Object instance, @NotNull Element element throw new XMLLoadException(exception); } } + + /** + * Loads an outline pane from a given element + * + * @param instance the instance class + * @param element the element + * @return the outline pane + * @deprecated this method is no longer used internally and has been superseded by + * {@link #load(Object, Element, Plugin)} + */ + @NotNull + @Deprecated + public static StaticPane load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(StaticPane.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButton.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButton.java index 709d70284..dbe60c9e9 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButton.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButton.java @@ -5,7 +5,10 @@ import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.pane.Pane; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.w3c.dom.Element; @@ -32,10 +35,35 @@ public class CycleButton extends Pane { */ private int position = 0; + /** + * Creates a new cycle button + * + * @param slot the slot of the button + * @param length the length of the button + * @param height the height of the button + * @param priority the priority of the button + * @since 0.10.8 + */ + public CycleButton(@NotNull Slot slot, int length, int height, @NotNull Priority priority) { + super(slot, length, height, priority); + } + public CycleButton(int x, int y, int length, int height, @NotNull Priority priority) { super(x, y, length, height, priority); } + /** + * Creates a new cycle button + * + * @param slot the slot of the button + * @param length the length of the button + * @param height the height of the button + * @since 0.10.8 + */ + public CycleButton(@NotNull Slot slot, int length, int height) { + super(slot, length, height); + } + public CycleButton(int x, int y, int length, int height) { super(x, y, length, height); } @@ -51,16 +79,25 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp int length = Math.min(this.length, maxLength); int height = Math.min(this.height, maxHeight); - int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY); + Slot paneSlot = getSlot(); + + int xPosition = paneSlot.getX(maxLength); + int yPosition = paneSlot.getY(maxLength); + + int totalLength = inventoryComponent.getLength(); - int x = adjustedSlot % inventoryComponent.getLength(); - int y = adjustedSlot / inventoryComponent.getLength(); + int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY); + + int x = adjustedSlot % totalLength; + int y = adjustedSlot / totalLength; //this isn't our item if (x < 0 || x >= length || y < 0 || y >= height) { return false; } + int previousPosition = position; + position++; if (position == panes.size()) { @@ -69,7 +106,8 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp callOnClick(event); - Pane pane = panes.get(position); + //use the previous position, since that will have the pane we clicked on + Pane pane = panes.get(previousPosition); pane.click(gui, inventoryComponent, event, slot, paneOffsetX + x, paneOffsetY + y, length, height); @@ -81,8 +119,10 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp @Override public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength, int maxHeight) { - int newX = paneOffsetX + x; - int newY = paneOffsetY + y; + Slot slot = getSlot(); + + int newX = paneOffsetX + slot.getX(maxLength); + int newY = paneOffsetY + slot.getY(maxLength); int newMaxLength = Math.min(maxLength, length); int newMaxHeight = Math.min(maxHeight, height); @@ -94,7 +134,7 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs @Contract(pure = true) @Override public CycleButton copy() { - CycleButton cycleButton = new CycleButton(x, y, length, height, getPriority()); + CycleButton cycleButton = new CycleButton(getSlot(), length, height, getPriority()); for (Pane pane : panes) { cycleButton.addPane(pane); @@ -162,11 +202,12 @@ public void cycle() { * * @param instance the instance class * @param element the element + * @param plugin the plugin that will be the owner of the underlying items * @return the cycle button - * @since 0.5.0 + * @since 0.10.8 */ @NotNull - public static CycleButton load(@NotNull Object instance, @NotNull Element element) { + public static CycleButton load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { int length; int height; @@ -194,9 +235,25 @@ public static CycleButton load(@NotNull Object instance, @NotNull Element elemen continue; } - cycleButton.addPane(Gui.loadPane(instance, pane)); + cycleButton.addPane(Gui.loadPane(instance, pane, plugin)); } return cycleButton; } + + /** + * Loads a cycle button from a given element + * + * @param instance the instance class + * @param element the element + * @return the cycle button + * @since 0.5.0 + * @deprecated this method is no longer used internally and has been superseded by + * {@link #load(Object, Element, Plugin)} + */ + @NotNull + @Deprecated + public static CycleButton load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(CycleButton.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Label.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Label.java index 205315ad2..fda4a04df 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Label.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Label.java @@ -6,12 +6,17 @@ import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; import com.github.stefvanschie.inventoryframework.font.util.Font; import com.github.stefvanschie.inventoryframework.pane.*; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.w3c.dom.Element; +import java.util.function.BiFunction; + /** * A label for displaying text. * @@ -31,6 +36,113 @@ public class Label extends OutlinePane { @NotNull private String text; + /** + * The plugin to be sed for creating items + */ + @NotNull + private final Plugin plugin; + + /** + * Creates a new label + * + * @param slot the slot + * @param length the length + * @param height the height + * @param priority the priority + * @param font the character set + * @param plugin the plugin that will be the owner for this label's items + * @see #Label(int, int, int, int, Priority, Font) + * @since 0.10.8 + */ + public Label(@NotNull Slot slot, int length, int height, @NotNull Priority priority, @NotNull Font font, + @NotNull Plugin plugin) { + super(slot, length, height); + + this.font = font; + this.text = ""; + + this.plugin = plugin; + + setPriority(priority); + } + + /** + * Creates a new label + * + * @param x the x coordinate + * @param y the y coordinate + * @param length the length + * @param height the height + * @param priority the priority + * @param font the character set + * @param plugin the plugin that will be the owner for this label's items + * @see #Label(int, int, int, int, Priority, Font) + * @since 0.10.8 + */ + public Label(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Font font, + @NotNull Plugin plugin) { + this(Slot.fromXY(x, y), length, height, priority, font, plugin); + } + + /** + * Creates a new label + * + * @param slot the slot + * @param length the length + * @param height the height + * @param font the character set + * @param plugin the plugin that will be the owner for this label's items + * @see #Label(int, int, int, int, Font) + * @since 0.10.8 + */ + public Label(@NotNull Slot slot, int length, int height, @NotNull Font font, @NotNull Plugin plugin) { + this(slot, length, height, Priority.NORMAL, font, plugin); + } + + /** + * Creates a new label + * + * @param x the x coordinate + * @param y the y coordinate + * @param length the length + * @param height the height + * @param font the character set + * @param plugin the plugin that will be the owner for this label's items + * @see #Label(int, int, int, int, Font) + * @since 0.10.8 + */ + public Label(int x, int y, int length, int height, @NotNull Font font, @NotNull Plugin plugin) { + this(x, y, length, height, Priority.NORMAL, font, plugin); + } + + /** + * Creates a new label + * + * @param length the length + * @param height the height + * @param font the character set + * @param plugin the plugin that will be the owner for this label's items + * @see #Label(int, int, Font) + * @since 0.10.8 + */ + public Label(int length, int height, @NotNull Font font, @NotNull Plugin plugin) { + this(0, 0, length, height, font, plugin); + } + + /** + * Creates a new label + * + * @param slot the slot + * @param length the length + * @param height the height + * @param priority the priority + * @param font the character set + * @since 0.10.8 + */ + public Label(@NotNull Slot slot, int length, int height, @NotNull Priority priority, @NotNull Font font) { + this(slot, length, height, priority, font, JavaPlugin.getProvidingPlugin(Label.class)); + } + /** * Creates a new label * @@ -43,9 +155,20 @@ public class Label extends OutlinePane { * @since 0.5.0 */ public Label(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Font font) { - this(x, y, length, height, font); + this(x, y, length, height, priority, font, JavaPlugin.getProvidingPlugin(Label.class)); + } - setPriority(priority); + /** + * Creates a new label + * + * @param slot the slot + * @param length the length + * @param height the height + * @param font the character set + * @since 0.10.8 + */ + public Label(@NotNull Slot slot, int length, int height, @NotNull Font font) { + this(slot, length, height, Priority.NORMAL, font); } /** @@ -59,10 +182,7 @@ public Label(int x, int y, int length, int height, @NotNull Priority priority, @ * @since 0.5.0 */ public Label(int x, int y, int length, int height, @NotNull Font font) { - this(length, height, font); - - this.x = x; - this.y = y; + this(x, y, length, height, Priority.NORMAL, font); } /** @@ -74,19 +194,22 @@ public Label(int x, int y, int length, int height, @NotNull Font font) { * @since 0.5.0 */ public Label(int length, int height, @NotNull Font font) { - super(length, height); - - this.font = font; - this.text = ""; + this(0, 0, length, height, font); } /** - * Sets the text to be displayed in this label + * Sets the text to be displayed in this label. If this label already had text, this text will be overwritten. The + * specified processor will be called for each character that is part of the specified text. The provided character + * will be the original character that was attempted to be shown - it is not subject to any transformations that may + * be applied for finding a valid item corresponding to this character, such as capitalization changes. * * @param text the new text - * @since 0.5.0 + * @param processor processes each character before using them + * @since 0.10.4 */ - public void setText(@NotNull String text) { + public void setText(@NotNull String text, + @NotNull BiFunction processor) { this.text = text; clear(); @@ -106,15 +229,26 @@ public void setText(@NotNull String text) { item = font.getDefaultItem(); } - addItem(new GuiItem(item)); + addItem(processor.apply(character, item.clone())); } } + /** + * Sets the text to be displayed in this label. If this label already had text, this text will be overwritten. + * + * @param text the new text + * @see #setText(String, BiFunction) + * @since 0.5.0 + */ + public void setText(@NotNull String text) { + setText(text, (character, item) -> new GuiItem(item, this.plugin)); + } + @NotNull @Contract(pure = true) @Override public Label copy() { - Label label = new Label(x, y, length, height, getPriority(), font); + Label label = new Label(getSlot(), length, height, getPriority(), font, this.plugin); for (GuiItem item : getItems()) { label.addItem(item.copy()); @@ -174,12 +308,14 @@ public Font getFont() { * Loads a label from a given element * * @param instance the instance class - * @param element the element + * @param element the element + * @param plugin the plugin that will be the owner of the underlying items * @return the percentage bar + * @since 0.10.8 */ @NotNull @Contract(pure = true) - public static Label load(@NotNull Object instance, @NotNull Element element) { + public static Label load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { int length; int height; @@ -200,7 +336,7 @@ public static Label load(@NotNull Object instance, @NotNull Element element) { throw new XMLLoadException("Incorrect font specified for label"); } - Label label = new Label(length, height, font); + Label label = new Label(length, height, font, plugin); Pane.load(label, instance, element); Orientable.load(label, element); @@ -217,4 +353,20 @@ public static Label load(@NotNull Object instance, @NotNull Element element) { return label; } + + /** + * Loads a label from a given element + * + * @param instance the instance class + * @param element the element + * @return the percentage bar + * @deprecated this method is no longer used internally and has been superseded by + * {@link #load(Object, Element, Plugin)} + */ + @NotNull + @Contract(pure = true) + @Deprecated + public static Label load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(Label.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PagingButtons.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PagingButtons.java new file mode 100644 index 000000000..7458b5999 --- /dev/null +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PagingButtons.java @@ -0,0 +1,395 @@ +package com.github.stefvanschie.inventoryframework.pane.component; + +import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; +import com.github.stefvanschie.inventoryframework.gui.GuiItem; +import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; +import com.github.stefvanschie.inventoryframework.pane.PaginatedPane; +import com.github.stefvanschie.inventoryframework.pane.Pane; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; +import org.bukkit.Material; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.w3c.dom.Element; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; + +/** + * An interface for interacting with {@link PaginatedPane}s. This gives two buttons for navigating backwards and + * forwards through the pages of the {@link PaginatedPane}. The backward button will be displayed at (0, 0) of this pane + * and the forward button will be displayed at (length - 1, 0) of this pane. If the paginated pane is at the first page + * or the last page, the backwards respectively the forward button will not show. This does not display the + * {@link PaginatedPane} itself, but is merely an interface for interacting with it. + * + * @since 0.10.14 + */ +public class PagingButtons extends Pane { + + /** + * The paginated pane. + */ + @NotNull + private final PaginatedPane pages; + + /** + * The backwards button. + */ + @NotNull + private GuiItem backwardButton; + + /** + * The forwards button. + */ + @NotNull + private GuiItem forwardButton; + + /** + * The plugin with which the items were created. + */ + @NotNull + private final Plugin plugin; + + /** + * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward + * item will be an arrow. If the length provided is less than 2, this will throw an + * {@link IllegalArgumentException}. + * + * @param slot the position of this interface + * @param length the length of this interface + * @param priority the priority of this interface + * @param pages the pages to interact with + * @param plugin the plugin that will be the owner of this interface's items + * @since 0.10.14 + * @throws IllegalArgumentException if the length is less than 2 + */ + public PagingButtons( + @NotNull Slot slot, + int length, + @NotNull Priority priority, + @NotNull PaginatedPane pages, + @NotNull Plugin plugin + ) { + super(slot, length, 1, priority); + + if (length < 2) { + throw new IllegalArgumentException("Length of paging buttons must be at least 2"); + } + + this.pages = pages; + this.plugin = plugin; + + this.backwardButton = new GuiItem(new ItemStack(Material.ARROW), plugin); + this.forwardButton = new GuiItem(new ItemStack(Material.ARROW), plugin); + } + + /** + * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward + * item will be an arrow. If the length provided is less than 2, this will throw an + * {@link IllegalArgumentException}. + * + * @param slot the position of this interface + * @param length the length of this interface + * @param priority the priority of this interface + * @param pages the pages to interact with + * @since 0.10.14 + * @throws IllegalArgumentException if the length is less than 2 + */ + public PagingButtons(@NotNull Slot slot, int length, @NotNull Priority priority, @NotNull PaginatedPane pages) { + this(slot, length, priority, pages, JavaPlugin.getProvidingPlugin(PagingButtons.class)); + } + + /** + * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward + * item will be an arrow. If the length provided is less than 2, this will throw an + * {@link IllegalArgumentException}. + * + * @param slot the position of this interface + * @param length the length of this interface + * @param pages the pages to interact with + * @param plugin the plugin that will be the owner of this interface's items + * @since 0.10.14 + * @throws IllegalArgumentException if the length is less than 2 + */ + public PagingButtons(@NotNull Slot slot, int length, @NotNull PaginatedPane pages, @NotNull Plugin plugin) { + this(slot, length, Priority.NORMAL, pages, plugin); + } + + /** + * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward + * item will be an arrow. If the length provided is less than 2, this will throw an + * {@link IllegalArgumentException}. + * + * @param slot the position of this interface + * @param length the length of this interface + * @param pages the pages to interact with + * @since 0.10.14 + * @throws IllegalArgumentException if the length is less than 2 + */ + public PagingButtons(@NotNull Slot slot, int length, @NotNull PaginatedPane pages) { + this(slot, length, Priority.NORMAL, pages); + } + + /** + * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward + * item will be an arrow. If the length provided is less than 2, this will throw an + * {@link IllegalArgumentException}. + * + * @param length the length of this interface + * @param priority the priority of this interface + * @param pages the pages to interact with + * @param plugin the plugin that will be the owner of this interface's items + * @since 0.10.14 + * @throws IllegalArgumentException if the length is less than 2 + */ + public PagingButtons(int length, @NotNull Priority priority, @NotNull PaginatedPane pages, @NotNull Plugin plugin) { + this(Slot.fromXY(0, 0), length, priority, pages, plugin); + } + + /** + * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward + * item will be an arrow. If the length provided is less than 2, this will throw an + * {@link IllegalArgumentException}. + * + * @param length the length of this interface + * @param priority the priority of this interface + * @param pages the pages to interact with + * @since 0.10.14 + * @throws IllegalArgumentException if the length is less than 2 + */ + public PagingButtons(int length, @NotNull Priority priority, @NotNull PaginatedPane pages) { + this(Slot.fromXY(0, 0), length, priority, pages, JavaPlugin.getProvidingPlugin(PagingButtons.class)); + } + + /** + * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward + * item will be an arrow. If the length provided is less than 2, this will throw an + * {@link IllegalArgumentException}. + * + * @param length the length of this interface + * @param pages the pages to interact with + * @param plugin the plugin that will be the owner of this interface's items + * @since 0.10.14 + * @throws IllegalArgumentException if the length is less than 2 + */ + public PagingButtons(int length, @NotNull PaginatedPane pages, @NotNull Plugin plugin) { + this(Slot.fromXY(0, 0), length, Priority.NORMAL, pages, plugin); + } + + /** + * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward + * item will be an arrow. If the length provided is less than 2, this will throw an + * {@link IllegalArgumentException}. + * + * @param length the length of this interface + * @param pages the pages to interact with + * @since 0.10.14 + * @throws IllegalArgumentException if the length is less than 2 + */ + public PagingButtons(int length, @NotNull PaginatedPane pages) { + this(Slot.fromXY(0, 0), length, Priority.NORMAL, pages); + } + + @Override + public boolean click( + @NotNull Gui gui, + @NotNull InventoryComponent inventoryComponent, + @NotNull InventoryClickEvent event, + int slot, + int paneOffsetX, + int paneOffsetY, + int maxLength, + int maxHeight + ) { + int length = Math.min(this.length, maxLength); + int height = Math.min(this.height, maxHeight); + + Slot paneSlot = getSlot(); + + int xPosition = paneSlot.getX(maxLength); + int yPosition = paneSlot.getY(maxLength); + + int totalLength = inventoryComponent.getLength(); + + int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY); + + int x = adjustedSlot % totalLength; + int y = adjustedSlot / totalLength; + + //this isn't our item + if (x < 0 || x >= length || y < 0 || y >= height) { + return false; + } + + callOnClick(event); + + ItemStack itemStack = event.getCurrentItem(); + + if (itemStack == null) { + return false; + } + + if (matchesItem(this.backwardButton, itemStack)) { + this.pages.setPage(this.pages.getPage() - 1); + + this.backwardButton.callAction(event); + + gui.update(); + + return true; + } + + if (matchesItem(this.forwardButton, itemStack)) { + this.pages.setPage(this.pages.getPage() + 1); + + this.forwardButton.callAction(event); + + gui.update(); + + return true; + } + + return false; + } + + @Override + public void display( + @NotNull InventoryComponent inventoryComponent, + int paneOffsetX, + int paneOffsetY, + int maxLength, + int maxHeight + ) { + int length = Math.min(getLength(), maxLength); + + int x = super.slot.getX(length) + paneOffsetX; + int y = super.slot.getY(length) + paneOffsetY; + + if (this.pages.getPage() > 0) { + inventoryComponent.setItem(this.backwardButton, x, y); + } + + if (this.pages.getPage() < this.pages.getPages() - 1) { + inventoryComponent.setItem(this.forwardButton, x + length - 1, y); + } + } + + /** + * {@inheritDoc} + * + * This does not make a copy of the {@link PaginatedPane} that is being controlled by this interface. + */ + @NotNull + @Contract(pure = true) + @Override + public PagingButtons copy() { + PagingButtons pagingButtons = new PagingButtons(getSlot(), getLength(), getPriority(), this.pages, this.plugin); + + pagingButtons.setVisible(isVisible()); + pagingButtons.onClick = super.onClick; + + pagingButtons.uuid = super.uuid; + + pagingButtons.backwardButton = this.backwardButton.copy(); + pagingButtons.forwardButton = this.forwardButton.copy(); + + return pagingButtons; + } + + @NotNull + @Contract(pure = true) + @Override + public Collection getItems() { + Collection items = new HashSet<>(); + + items.add(this.backwardButton); + items.add(this.forwardButton); + + return Collections.unmodifiableCollection(items); + } + + /** + * Sets the item to be used for navigating backwards. If an event is attached to the item, this event will be called + * after the page has been changed. + * + * @param item the new backward item + * @since 0.10.14 + */ + public void setBackwardButton(@NotNull GuiItem item) { + this.backwardButton = item; + } + + /** + * Sets the item to be used for navigating forwards. If an event is attached to the item, this event will be called + * after the page has been changed. + * + * @param item the new forward item + * @since 0.10.14 + */ + public void setForwardButton(@NotNull GuiItem item) { + this.forwardButton = item; + } + + @NotNull + @Contract(pure = true) + @Override + public Collection getPanes() { + return Collections.emptySet(); + } + + /** + * This is a no-op. + * + * @since 0.10.14 + */ + @Override + public void clear() {} + + /** + * Loads a paging buttons pane from an XML element. + * + * @param instance the instance class + * @param element the element + * @param plugin the plugin that will be the owner of the underlying items + * @return the paging buttons pane + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public static PagingButtons load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { + int length; + + try { + length = Integer.parseInt(element.getAttribute("length")); + } catch (NumberFormatException exception) { + throw new XMLLoadException(exception); + } + + if (!element.hasAttribute("pages")) { + throw new XMLLoadException("Paging buttons does not have pages attribute"); + } + + Element paginatedPaneElement = element.getOwnerDocument().getElementById(element.getAttribute("pages")); + + if (paginatedPaneElement == null) { + throw new XMLLoadException("Paging buttons pages reference is invalid"); + } + + Object paginatedPane = paginatedPaneElement.getUserData("pane"); + + if (!(paginatedPane instanceof PaginatedPane)) { + throw new XMLLoadException("Retrieved data is not a paginated pane"); + } + + PagingButtons pagingButtons = new PagingButtons(length, (PaginatedPane) paginatedPane); + + Pane.load(pagingButtons, instance, element); + + return pagingButtons; + } +} diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PercentageBar.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PercentageBar.java index 49340a8cd..b4729c885 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PercentageBar.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PercentageBar.java @@ -7,7 +7,10 @@ import com.github.stefvanschie.inventoryframework.pane.Orientable; import com.github.stefvanschie.inventoryframework.pane.Pane; import com.github.stefvanschie.inventoryframework.pane.component.util.VariableBar; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.w3c.dom.Element; @@ -19,10 +22,104 @@ */ public class PercentageBar extends VariableBar { + /** + * Creates a new percentage bar + * + * @param slot the slot of the bar + * @param length the length of the bar + * @param height the height of the bar + * @param priority the priority of the bar + * @param plugin the plugin that will be the owner for this percentage bar's items + * @since 0.10.8 + */ + public PercentageBar(@NotNull Slot slot, int length, int height, @NotNull Priority priority, + @NotNull Plugin plugin) { + super(slot, length, height, priority, plugin); + } + + /** + * Creates a new percentage bar + * + * @param x the x coordinate of the bar + * @param y the y coordinate of the bar + * @param length the length of the bar + * @param height the height of the bar + * @param priority the priority of the bar + * @param plugin the plugin that will be the owner for this percentage bar's items + * @since 0.10.8 + */ + public PercentageBar(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) { + super(x, y, length, height, priority, plugin); + } + + /** + * Creates a new percentage bar + * + * @param slot the slot of the bar + * @param length the length of the bar + * @param height the height of the bar + * @param plugin the plugin that will be the owner for this percentage bar's items + * @since 0.10.8 + */ + public PercentageBar(@NotNull Slot slot, int length, int height, @NotNull Plugin plugin) { + super(slot, length, height, plugin); + } + + /** + * Creates a new percentage bar + * + * @param x the x coordinate of the bar + * @param y the y coordinate of the bar + * @param length the length of the bar + * @param height the height of the bar + * @param plugin the plugin that will be the owner for this percentage bar's items + * @since 0.10.8 + */ + public PercentageBar(int x, int y, int length, int height, @NotNull Plugin plugin) { + super(x, y, length, height, plugin); + } + + /** + * Creates a new percentage bar + * + * @param length the length of the bar + * @param height the height of the bar + * @param plugin the plugin that will be the owner for this percentage bar's items + * @since 0.10.8 + */ + public PercentageBar(int length, int height, @NotNull Plugin plugin) { + super(length, height, plugin); + } + + /** + * Creates a new percentage bar + * + * @param slot the slot of the bar + * @param length the length of the bar + * @param height the height of the bar + * @param priority the priority of the bar + * @since 0.10.8 + */ + public PercentageBar(@NotNull Slot slot, int length, int height, @NotNull Priority priority) { + super(slot, length, height, priority); + } + public PercentageBar(int x, int y, int length, int height, @NotNull Priority priority) { super(x, y, length, height, priority); } + /** + * Creates a new percentage bar + * + * @param slot the slot of the bar + * @param length the length of the bar + * @param height the height of the bar + * @since 0.10.8 + */ + public PercentageBar(@NotNull Slot slot, int length, int height) { + super(slot, length, height); + } + public PercentageBar(int x, int y, int length, int height) { super(x, y, length, height); } @@ -38,10 +135,17 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp int length = Math.min(this.length, maxLength); int height = Math.min(this.height, maxHeight); - int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY); + Slot paneSlot = getSlot(); + + int xPosition = paneSlot.getX(maxLength); + int yPosition = paneSlot.getY(maxLength); + + int totalLength = inventoryComponent.getLength(); + + int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY); - int x = adjustedSlot % inventoryComponent.getLength(); - int y = adjustedSlot / inventoryComponent.getLength(); + int x = adjustedSlot % totalLength; + int y = adjustedSlot / totalLength; if (x < 0 || x >= length || y < 0 || y >= height) { return false; @@ -51,8 +155,8 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp event.setCancelled(true); - int newPaneOffsetX = paneOffsetX + getX(); - int newPaneOffsetY = paneOffsetY + getY(); + int newPaneOffsetX = paneOffsetX + xPosition; + int newPaneOffsetY = paneOffsetY + yPosition; return this.fillPane.click( @@ -79,7 +183,7 @@ public void setPercentage(float percentage) { @Contract(pure = true) @Override public PercentageBar copy() { - PercentageBar percentageBar = new PercentageBar(x, y, length, height, getPriority()); + PercentageBar percentageBar = new PercentageBar(getSlot(), length, height, getPriority()); applyContents(percentageBar); @@ -100,12 +204,14 @@ public float getPercentage() { * Loads a percentage bar from a given element * * @param instance the instance class - * @param element the element + * @param element the element + * @param plugin the plugin that will be the owner of the underlying items * @return the percentage bar + * @since 0.10.8 */ @NotNull @Contract(pure = true) - public static PercentageBar load(@NotNull Object instance, @NotNull Element element) { + public static PercentageBar load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { int length; int height; @@ -116,7 +222,7 @@ public static PercentageBar load(@NotNull Object instance, @NotNull Element elem throw new XMLLoadException(exception); } - PercentageBar percentageBar = new PercentageBar(length, height); + PercentageBar percentageBar = new PercentageBar(length, height, plugin); Pane.load(percentageBar, instance, element); Orientable.load(percentageBar, element); @@ -136,4 +242,20 @@ public static PercentageBar load(@NotNull Object instance, @NotNull Element elem return percentageBar; } + + /** + * Loads a percentage bar from a given element + * + * @param instance the instance class + * @param element the element + * @return the percentage bar + * @deprecated this method is no longer used internally and has been superseded by + * {@link #load(Object, Element, Plugin)} + */ + @NotNull + @Contract(pure = true) + @Deprecated + public static PercentageBar load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(PercentageBar.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Slider.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Slider.java index 17e873ff7..26c74e3d2 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Slider.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Slider.java @@ -7,7 +7,10 @@ import com.github.stefvanschie.inventoryframework.pane.Orientable; import com.github.stefvanschie.inventoryframework.pane.Pane; import com.github.stefvanschie.inventoryframework.pane.component.util.VariableBar; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.w3c.dom.Element; @@ -19,10 +22,103 @@ */ public class Slider extends VariableBar { + /** + * Creates a new slider + * + * @param slot the slot of the slider + * @param length the length of the slider + * @param height the height of the slider + * @param priority the priority of the slider + * @param plugin the plugin that will be the owner of the slider's items + * @since 0.10.8 + */ + public Slider(@NotNull Slot slot, int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) { + super(slot, length, height, priority, plugin); + } + + /** + * Creates a new slider + * + * @param x the x coordinate of the slider + * @param y the y coordinate of the slier + * @param length the length of the slider + * @param height the height of the slider + * @param priority the priority of the slider + * @param plugin the plugin that will be the owner of the slider's items + * @since 0.10.8 + */ + public Slider(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) { + super(x, y, length, height, priority, plugin); + } + + /** + * Creates a new slider + * + * @param slot the slot of the slider + * @param length the length of the slider + * @param height the height of the slider + * @param plugin the plugin that will be the owner of the slider's items + * @since 0.10.8 + */ + public Slider(@NotNull Slot slot, int length, int height, @NotNull Plugin plugin) { + super(slot, length, height, plugin); + } + + /** + * Creates a new slider + * + * @param x the x coordinate of the slider + * @param y the y coordinate of the slier + * @param length the length of the slider + * @param height the height of the slider + * @param plugin the plugin that will be the owner of the slider's items + * @since 0.10.8 + */ + public Slider(int x, int y, int length, int height, @NotNull Plugin plugin) { + super(x, y, length, height, plugin); + } + + /** + * Creates a new slider + * + * @param length the length of the slider + * @param height the height of the slider + * @param plugin the plugin that will be the owner of the slider's items + * @since 0.10.8 + */ + public Slider(int length, int height, @NotNull Plugin plugin) { + super(length, height, plugin); + } + + /** + * Creates a new slider + * + * @param slot the slot of the slider + * @param length the length of the slider + * @param height the height of the slider + * @param priority the priority of the slider + * @since 0.10.8 + */ + public Slider(@NotNull Slot slot, int length, int height, @NotNull Priority priority) { + super(slot, length, height, priority); + } + public Slider(int x, int y, int length, int height, @NotNull Priority priority) { super(x, y, length, height, priority); } + /** + * Creates a new slider + * + * @param slot the slot of the slider + * @param length the length of the slider + * @param height the height of the slider + * @since 0.10.8 + */ + public Slider(@NotNull Slot slot, int length, int height) { + super(slot, length, height); + } + public Slider(int x, int y, int length, int height) { super(x, y, length, height); } @@ -38,10 +134,17 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp int length = Math.min(this.length, maxLength); int height = Math.min(this.height, maxHeight); - int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY); + Slot paneSlot = getSlot(); + + int xPosition = paneSlot.getX(maxLength); + int yPosition = paneSlot.getY(maxLength); + + int totalLength = inventoryComponent.getLength(); + + int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY); - int x = adjustedSlot % inventoryComponent.getLength(); - int y = adjustedSlot / inventoryComponent.getLength(); + int x = adjustedSlot % totalLength; + int y = adjustedSlot / totalLength; if (x < 0 || x >= length || y < 0 || y >= height) { return false; @@ -57,8 +160,8 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp callOnClick(event); - int newPaneOffsetX = paneOffsetX + getX(); - int newPaneOffsetY = paneOffsetY + getY(); + int newPaneOffsetX = paneOffsetX + xPosition; + int newPaneOffsetY = paneOffsetY + yPosition; boolean success = this.fillPane.click( gui, inventoryComponent, event, slot, newPaneOffsetX, newPaneOffsetY, length, height @@ -88,7 +191,7 @@ public void setValue(float value) { @Contract(pure = true) @Override public Slider copy() { - Slider slider = new Slider(x, y, length, height, getPriority()); + Slider slider = new Slider(getSlot(), length, height, getPriority()); applyContents(slider); @@ -109,12 +212,14 @@ public float getValue() { * Loads a percentage bar from a given element * * @param instance the instance class - * @param element the element + * @param element the element + * @param plugin the plugin that will be the owner of the udnerlying items * @return the percentage bar + * @since 0.10.8 */ @NotNull @Contract(pure = true) - public static Slider load(@NotNull Object instance, @NotNull Element element) { + public static Slider load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { int length; int height; @@ -125,7 +230,7 @@ public static Slider load(@NotNull Object instance, @NotNull Element element) { throw new XMLLoadException(exception); } - Slider slider = new Slider(length, height); + Slider slider = new Slider(length, height, plugin); Pane.load(slider, instance, element); Orientable.load(slider, element); @@ -145,4 +250,20 @@ public static Slider load(@NotNull Object instance, @NotNull Element element) { return slider; } + + /** + * Loads a percentage bar from a given element + * + * @param instance the instance class + * @param element the element + * @return the percentage bar + * @deprecated this method is no longer used internally and has been superseded by + * {@link #load(Object, Element, Plugin)} + */ + @NotNull + @Contract(pure = true) + @Deprecated + public static Slider load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(Slider.class)); + } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/ToggleButton.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/ToggleButton.java index da25e0435..6ff5bd43a 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/ToggleButton.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/ToggleButton.java @@ -1,221 +1,499 @@ -package com.github.stefvanschie.inventoryframework.pane.component; - -import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; -import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; -import com.github.stefvanschie.inventoryframework.gui.GuiItem; -import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; -import com.github.stefvanschie.inventoryframework.pane.OutlinePane; -import com.github.stefvanschie.inventoryframework.pane.Pane; -import org.bukkit.Material; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.w3c.dom.Element; - -import java.util.Collection; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * A button that toggles between an enabled and disabled state. - * - * @since 0.5.0 - */ -public class ToggleButton extends Pane { - - /** - * The panes used for showing the enabled and disabled states - */ - private final OutlinePane enabledPane, disabledPane; - - /** - * Whether the button is enabled or disabled - */ - private boolean enabled = false; - - public ToggleButton(int x, int y, int length, int height, @NotNull Priority priority) { - this(x, y, length, height); - - setPriority(priority); - } - - public ToggleButton(int length, int height) { - super(length, height); - - this.enabledPane = new OutlinePane(0, 0, length, height); - this.enabledPane.addItem(new GuiItem(new ItemStack(Material.GREEN_STAINED_GLASS_PANE))); - this.enabledPane.setRepeat(true); - - this.disabledPane = new OutlinePane(0, 0, length, height); - this.disabledPane.addItem(new GuiItem(new ItemStack(Material.RED_STAINED_GLASS_PANE))); - this.disabledPane.setRepeat(true); - } - - public ToggleButton(int x, int y, int length, int height) { - this(length, height); - - setX(x); - setY(y); - } - - @Override - public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength, - int maxHeight) { - int newX = paneOffsetX + x; - int newY = paneOffsetY + y; - - int newMaxLength = Math.min(maxLength, length); - int newMaxHeight = Math.min(maxHeight, height); - - if (enabled) { - enabledPane.display(inventoryComponent, newX, newY, newMaxLength, newMaxHeight); - } else { - disabledPane.display(inventoryComponent, newX, newY, newMaxLength, newMaxHeight); - } - } - - @Override - public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent, - @NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength, - int maxHeight) { - int length = Math.min(this.length, maxLength); - int height = Math.min(this.height, maxHeight); - - int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY); - - int x = adjustedSlot % inventoryComponent.getLength(); - int y = adjustedSlot / inventoryComponent.getLength(); - - //this isn't our item - if (x < 0 || x >= length || y < 0 || y >= height) { - return false; - } - - toggle(); - - callOnClick(event); - - int newX = paneOffsetX + x; - int newY = paneOffsetY + y; - - if (enabled) { - enabledPane.click(gui, inventoryComponent, event, slot, newX, newY, length, height); - } else { - disabledPane.click(gui, inventoryComponent, event, slot, newX, newY, length, height); - } - - gui.update(); - - return true; - } - - @NotNull - @Contract(pure = true) - @Override - public ToggleButton copy() { - ToggleButton toggleButton = new ToggleButton(x, y, length, height, getPriority()); - - toggleButton.setVisible(isVisible()); - toggleButton.onClick = onClick; - - toggleButton.uuid = uuid; - - toggleButton.setEnabledItem(enabledPane.getItems().get(0).copy()); - toggleButton.setDisabledItem(disabledPane.getItems().get(0).copy()); - - toggleButton.enabled = enabled; - - return toggleButton; - } - - /** - * Sets the item to use when the button is set to disabled - * - * @param item the disabled item - * @since 0.5.0 - */ - public void setDisabledItem(@NotNull GuiItem item) { - disabledPane.clear(); - - disabledPane.addItem(item); - } - - /** - * Sets the item to use when the button is set to enabled - * - * @param item the enabled item - * @since 0.5.0 - */ - public void setEnabledItem(@NotNull GuiItem item) { - enabledPane.clear(); - - enabledPane.addItem(item); - } - - @NotNull - @Override - public Collection getItems() { - return getPanes().stream().flatMap(pane -> pane.getItems().stream()).collect(Collectors.toSet()); - } - - @NotNull - @Override - public Collection getPanes() { - return Stream.of(enabledPane, disabledPane).collect(Collectors.toSet()); - } - - /** - * Gets whether this toggle button is currently enabled or disabled. - * - * @return whether the button is enabled or disabled - * @since 0.9.6 - */ - @Contract(pure = true) - public boolean isEnabled() { - return enabled; - } - - /** - * Toggles between the enabled and disabled states - * - * @since 0.5.0 - */ - public void toggle() { - enabled = !enabled; - } - - @Override - public void clear() {} - - /** - * Loads a toggle button from an XML element - * - * @param instance the instance class - * @param element the element - * @return the toggle button - * @since 0.5.0 - */ - @NotNull - @Contract(pure = true) - public static ToggleButton load(@NotNull Object instance, @NotNull Element element) { - int length, height; - - try { - length = Integer.parseInt(element.getAttribute("length")); - height = Integer.parseInt(element.getAttribute("height")); - } catch (NumberFormatException exception) { - throw new XMLLoadException(exception); - } - - ToggleButton toggleButton = new ToggleButton(length, height); - - Pane.load(toggleButton, instance, element); - - if (element.hasAttribute("enabled") && Boolean.parseBoolean(element.getAttribute("enabled"))) { - toggleButton.toggle(); - } - - return toggleButton; - } -} +package com.github.stefvanschie.inventoryframework.pane.component; + +import com.github.stefvanschie.inventoryframework.gui.InventoryComponent; +import com.github.stefvanschie.inventoryframework.gui.type.util.Gui; +import com.github.stefvanschie.inventoryframework.gui.GuiItem; +import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; +import com.github.stefvanschie.inventoryframework.pane.OutlinePane; +import com.github.stefvanschie.inventoryframework.pane.Pane; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; +import org.bukkit.Material; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.w3c.dom.Element; + +import java.util.Collection; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * A button that toggles between an enabled and disabled state. + * + * @since 0.5.0 + */ +public class ToggleButton extends Pane { + + /** + * The panes used for showing the enabled and disabled states + */ + private final OutlinePane enabledPane, disabledPane; + + /** + * Whether the button is enabled or disabled + */ + private boolean enabled; + + /** + * Whether this button can be toggled by a player + */ + private boolean allowToggle = true; + + /** + * Creates a new toggle button + * + * @param slot the slot + * @param length the length + * @param height the height + * @param priority the priority + * @param enabled whether the button should start in its enabled or disabled state + * @param plugin the plugin that will be the owner of this button's items + * @see #ToggleButton(int, int, int, int, Priority, boolean) + * @since 0.10.8 + */ + public ToggleButton(@NotNull Slot slot, int length, int height, @NotNull Priority priority, boolean enabled, + @NotNull Plugin plugin) { + super(slot, length, height, priority); + + this.enabled = enabled; + + this.enabledPane = new OutlinePane(length, height); + this.enabledPane.addItem(new GuiItem(new ItemStack(Material.GREEN_STAINED_GLASS_PANE), plugin)); + this.enabledPane.setRepeat(true); + + this.disabledPane = new OutlinePane(length, height); + this.disabledPane.addItem(new GuiItem(new ItemStack(Material.RED_STAINED_GLASS_PANE), plugin)); + this.disabledPane.setRepeat(true); + } + + /** + * Creates a new toggle button + * + * @param x the x coordinate + * @param y the y coordinate + * @param length the length + * @param height the height + * @param priority the priority + * @param enabled whether the button should start in its enabled or disabled state + * @param plugin the plugin that will be the owner of this button's items + * @see #ToggleButton(int, int, int, int, Priority, boolean) + * @since 0.10.8 + */ + public ToggleButton(int x, int y, int length, int height, @NotNull Priority priority, boolean enabled, + @NotNull Plugin plugin) { + this(Slot.fromXY(x, y), length, height, priority, enabled, plugin); + } + + /** + * Creates a new toggle button + * + * @param slot the slot + * @param length the length + * @param height the height + * @param priority the priority + * @param plugin the plugin that will be the owner of this button's items + * @see #ToggleButton(Slot, int, int, Priority) + * @since 0.10.8 + */ + public ToggleButton(@NotNull Slot slot, int length, int height, @NotNull Priority priority, + @NotNull Plugin plugin) { + this(slot, length, height, priority, false, plugin); + } + + /** + * Creates a new toggle button + * + * @param x the x coordinate + * @param y the y coordinate + * @param length the length + * @param height the height + * @param priority the priority + * @param plugin the plugin that will be the owner of this button's items + * @see #ToggleButton(int, int, int, int, Priority) + * @since 0.10.8 + */ + public ToggleButton(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) { + this(x, y, length, height, priority, false, plugin); + } + + /** + * Creates a new toggle button + * + * @param slot the slot + * @param length the length + * @param height the height + * @param enabled whether the button should start in its enabled or disabled state + * @param plugin the plugin that will be the owner of this button's items + * @see #ToggleButton(Slot, int, int, boolean) + * @since 0.10.8 + */ + public ToggleButton(@NotNull Slot slot, int length, int height, boolean enabled, @NotNull Plugin plugin) { + this(slot, length, height, Priority.NORMAL, enabled, plugin); + } + + /** + * Creates a new toggle button + * + * @param x the x coordinate + * @param y the y coordinate + * @param length the length + * @param height the height + * @param enabled whether the button should start in its enabled or disabled state + * @param plugin the plugin that will be the owner of this button's items + * @see #ToggleButton(int, int, int, int, boolean) + * @since 0.10.8 + */ + public ToggleButton(int x, int y, int length, int height, boolean enabled, @NotNull Plugin plugin) { + this(x, y, length, height, Priority.NORMAL, enabled, plugin); + } + + /** + * Creates a new toggle button + * + * @param slot the slot + * @param length the length + * @param height the height + * @param plugin the plugin that will be the owner of this button's items + * @see #ToggleButton(Slot, int, int) + * @since 0.10.8 + */ + public ToggleButton(@NotNull Slot slot, int length, int height, @NotNull Plugin plugin) { + this(slot, length, height, false, plugin); + } + + /** + * Creates a new toggle button + * + * @param x the x coordinate + * @param y the y coordinate + * @param length the length + * @param height the height + * @param plugin the plugin that will be the owner of this button's items + * @see #ToggleButton(int, int, int, int) + * @since 0.10.8 + */ + public ToggleButton(int x, int y, int length, int height, @NotNull Plugin plugin) { + this(x, y, length, height, false, plugin); + } + + /** + * Creates a new toggle button + * + * @param length the length + * @param height the height + * @param enabled whether the button should start in its enabled or disabled state + * @param plugin the plugin that will be the owner of this button's items + * @see #ToggleButton(int, int, boolean) + * @since 0.10.8 + */ + public ToggleButton(int length, int height, boolean enabled, @NotNull Plugin plugin) { + this(0, 0, length, height, enabled); + } + + /** + * Creates a new toggle button + * + * @param length the length + * @param height the height + * @param plugin the plugin that will be the owner of this button's items + * @see #ToggleButton(int, int) + * @since 0.10.8 + */ + public ToggleButton(int length, int height, @NotNull Plugin plugin) { + this(length, height, false); + } + + /** + * Creates a new toggle button + * + * @param slot the slot + * @param length the length + * @param height the height + * @param priority the priority + * @param enabled whether the button should start in its enabled or disabled state + * @since 0.10.8 + */ + public ToggleButton(@NotNull Slot slot, int length, int height, @NotNull Priority priority, boolean enabled) { + this(slot, length, height, priority, enabled, JavaPlugin.getProvidingPlugin(ToggleButton.class)); + } + + public ToggleButton(int x, int y, int length, int height, @NotNull Priority priority, boolean enabled) { + this(x, y, length, height, priority, enabled, JavaPlugin.getProvidingPlugin(ToggleButton.class)); + } + + /** + * Creates a new toggle button + * + * @param slot the slot + * @param length the length + * @param height the height + * @param priority the priority + * @since 0.10.8 + */ + public ToggleButton(@NotNull Slot slot, int length, int height, @NotNull Priority priority) { + this(slot, length, height, priority, false); + } + + public ToggleButton(int x, int y, int length, int height, @NotNull Priority priority) { + this(x, y, length, height, priority, false); + } + + /** + * Creates a new toggle button + * + * @param slot the slot + * @param length the length + * @param height the height + * @param enabled whether the button should start in its enabled or disabled state + * @since 0.10.8 + */ + public ToggleButton(@NotNull Slot slot, int length, int height, boolean enabled) { + this(slot, length, height, Priority.NORMAL, enabled); + } + + public ToggleButton(int x, int y, int length, int height, boolean enabled) { + this(x, y, length, height, Priority.NORMAL, enabled); + } + + /** + * Creates a new toggle button + * + * @param slot the slot + * @param length the length + * @param height the height + * @since 0.10.8 + */ + public ToggleButton(@NotNull Slot slot, int length, int height) { + this(slot, length, height, false); + } + + public ToggleButton(int x, int y, int length, int height) { + this(x, y, length, height, false); + } + + public ToggleButton(int length, int height, boolean enabled) { + this(0, 0, length, height, enabled); + } + + public ToggleButton(int length, int height) { + this(length, height, false); + } + + @Override + public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength, + int maxHeight) { + int newMaxLength = Math.min(maxLength, length); + int newMaxHeight = Math.min(maxHeight, height); + + int newPaneOffsetX = this.slot.getX(newMaxLength) + paneOffsetX; + int newPaneOffsetY = this.slot.getY(newMaxHeight) + paneOffsetY; + + if (enabled) { + this.enabledPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, newMaxLength, newMaxHeight); + } else { + this.disabledPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, newMaxLength, newMaxHeight); + } + } + + @Override + public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent, + @NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength, + int maxHeight) { + int length = Math.min(this.length, maxLength); + int height = Math.min(this.height, maxHeight); + + Slot paneSlot = getSlot(); + + int xPosition = paneSlot.getX(maxLength); + int yPosition = paneSlot.getY(maxLength); + + int totalLength = inventoryComponent.getLength(); + + int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY); + + int x = adjustedSlot % totalLength; + int y = adjustedSlot / totalLength; + + //this isn't our item + if (x < 0 || x >= length || y < 0 || y >= height) { + return false; + } + + if (this.allowToggle) { + toggle(); + } + + callOnClick(event); + + int newX = paneOffsetX + xPosition; + int newY = paneOffsetY + yPosition; + + /* + Since we've toggled before, the click for the panes should be swapped around. If we haven't toggled due to + allowToggle being false, then we should click the pane corresponding to the current state. An XOR achieves this. + */ + if (enabled == this.allowToggle) { + disabledPane.click(gui, inventoryComponent, event, slot, newX, newY, length, height); + } else { + enabledPane.click(gui, inventoryComponent, event, slot, newX, newY, length, height); + } + + gui.update(); + + return true; + } + + @NotNull + @Contract(pure = true) + @Override + public ToggleButton copy() { + ToggleButton toggleButton = new ToggleButton(getSlot(), length, height, getPriority(), enabled); + + toggleButton.allowToggle = this.allowToggle; + + toggleButton.setVisible(isVisible()); + toggleButton.onClick = onClick; + + toggleButton.uuid = uuid; + + toggleButton.setEnabledItem(enabledPane.getItems().get(0).copy()); + toggleButton.setDisabledItem(disabledPane.getItems().get(0).copy()); + + return toggleButton; + } + + @Override + public void setLength(int length) { + super.setLength(length); + + this.disabledPane.setLength(length); + this.enabledPane.setLength(length); + } + + @Override + public void setHeight(int height) { + super.setHeight(height); + + this.disabledPane.setHeight(height); + this.enabledPane.setHeight(height); + } + + /** + * Sets the item to use when the button is set to disabled + * + * @param item the disabled item + * @since 0.5.0 + */ + public void setDisabledItem(@NotNull GuiItem item) { + disabledPane.clear(); + + disabledPane.addItem(item); + } + + /** + * Sets the item to use when the button is set to enabled + * + * @param item the enabled item + * @since 0.5.0 + */ + public void setEnabledItem(@NotNull GuiItem item) { + enabledPane.clear(); + + enabledPane.addItem(item); + } + + @NotNull + @Override + public Collection getItems() { + return getPanes().stream().flatMap(pane -> pane.getItems().stream()).collect(Collectors.toSet()); + } + + @NotNull + @Override + public Collection getPanes() { + return Stream.of(enabledPane, disabledPane).collect(Collectors.toSet()); + } + + /** + * Sets whether this toggle button can be toggled. This only prevents players from toggling the button and does not + * prevent toggling the button programmatically with methods such as {@link #toggle()}. + * + * @param allowToggle whether this button can be toggled + * @since 0.10.8 + */ + public void allowToggle(boolean allowToggle) { + this.allowToggle = allowToggle; + } + + /** + * Gets whether this toggle button is currently enabled or disabled. + * + * @return whether the button is enabled or disabled + * @since 0.9.6 + */ + @Contract(pure = true) + public boolean isEnabled() { + return enabled; + } + + /** + * Toggles between the enabled and disabled states + * + * @since 0.5.0 + */ + public void toggle() { + enabled = !enabled; + } + + @Override + public void clear() {} + + /** + * Loads a toggle button from an XML element + * + * @param instance the instance class + * @param element the element + * @param plugin the plugin that will be the owner of the underlying items + * @return the toggle button + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + public static ToggleButton load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) { + int length, height; + + try { + length = Integer.parseInt(element.getAttribute("length")); + height = Integer.parseInt(element.getAttribute("height")); + } catch (NumberFormatException exception) { + throw new XMLLoadException(exception); + } + + boolean enabled = element.hasAttribute("enabled") && Boolean.parseBoolean(element.getAttribute("enabled")); + ToggleButton toggleButton = new ToggleButton(length, height, enabled, plugin); + + Pane.load(toggleButton, instance, element); + + return toggleButton; + } + + /** + * Loads a toggle button from an XML element + * + * @param instance the instance class + * @param element the element + * @return the toggle button + * @since 0.5.0 + * @deprecated this method is no longer used internally and has been superseded by + * {@link #load(Object, Element, Plugin)} + */ + @NotNull + @Contract(pure = true) + @Deprecated + public static ToggleButton load(@NotNull Object instance, @NotNull Element element) { + return load(instance, element, JavaPlugin.getProvidingPlugin(ToggleButton.class)); + } +} diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/util/VariableBar.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/util/VariableBar.java index 1b07921fe..e3a2d0507 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/util/VariableBar.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/util/VariableBar.java @@ -6,8 +6,11 @@ import com.github.stefvanschie.inventoryframework.pane.Orientable; import com.github.stefvanschie.inventoryframework.pane.OutlinePane; import com.github.stefvanschie.inventoryframework.pane.Pane; +import com.github.stefvanschie.inventoryframework.pane.util.Slot; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -43,8 +46,33 @@ public abstract class VariableBar extends Pane implements Orientable, Flippable */ protected boolean flipHorizontally, flipVertically; - protected VariableBar(int length, int height) { - super(length, height); + /** + * Creates a new variable bar + * + * @param length the length of the bar + * @param height the height of the bar + * @param plugin the plugin that will be the owner for this variable bar's items + * @see #VariableBar(int, int) + * @since 0.10.8 + */ + protected VariableBar(int length, int height, @NotNull Plugin plugin) { + this(0, 0, length, height, plugin); + } + + /** + * Creates a new variable bar + * + * @param slot the slot of the bar + * @param length the length of the bar + * @param height the height of the bar + * @param priority the priority of the bar + * @param plugin the plugin that will be the owner for this variable bar's items + * @see #VariableBar(Slot, int, int, Priority) + * @since 0.10.8 + */ + protected VariableBar(@NotNull Slot slot, int length, int height, @NotNull Priority priority, + @NotNull Plugin plugin) { + super(slot, length, height); this.value = 0F; this.orientation = Orientation.HORIZONTAL; @@ -53,23 +81,94 @@ protected VariableBar(int length, int height) { this.backgroundPane = new OutlinePane(0, 0, length, height); this.fillPane.addItem(new GuiItem(new ItemStack(Material.GREEN_STAINED_GLASS_PANE), - event -> event.setCancelled(true))); + event -> event.setCancelled(true), plugin)); this.backgroundPane.addItem(new GuiItem(new ItemStack(Material.RED_STAINED_GLASS_PANE), - event -> event.setCancelled(true))); + event -> event.setCancelled(true), plugin)); this.fillPane.setRepeat(true); this.backgroundPane.setRepeat(true); this.fillPane.setVisible(false); + + setPriority(priority); } - protected VariableBar(int x, int y, int length, int height, @NotNull Priority priority) { - this(length, height); + /** + * Creates a new variable bar + * + * @param x the x coordinate of the bar + * @param y the y coordinate of the bar + * @param length the length of the bar + * @param height the height of the bar + * @param priority the priority of the bar + * @param plugin the plugin that will be the owner for this variable bar's items + * @see #VariableBar(int, int) + * @since 0.10.8 + */ + protected VariableBar(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) { + this(Slot.fromXY(x, y), length, height, priority, plugin); + } - setX(x); - setY(y); + /** + * Creates a new variable bar + * + * @param slot the slot of the bar + * @param length the length of the bar + * @param height the height of the bar + * @param plugin the plugin that will be the owner for this variable bar's items + * @see #VariableBar(Slot, int, int) + * @since 0.10.8 + */ + protected VariableBar(@NotNull Slot slot, int length, int height, @NotNull Plugin plugin) { + this(slot, length, height, Priority.NORMAL, plugin); + } - setPriority(priority); + /** + * Creates a new variable bar + * + * @param x the x coordinate of the bar + * @param y the y coordinate of the bar + * @param length the length of the bar + * @param height the height of the bar + * @param plugin the plugin that will be the owner for this variable bar's items + * @see #VariableBar(int, int) + * @since 0.10.8 + */ + protected VariableBar(int x, int y, int length, int height, @NotNull Plugin plugin) { + this(x, y, length, height, Priority.NORMAL, plugin); + } + + protected VariableBar(int length, int height) { + this(0, 0, length, height); + } + + /** + * Creates a new variable bar + * + * @param slot the slot of the bar + * @param length the length of the bar + * @param height the height of the bar + * @param priority the priority of the bar + * @since 0.10.8 + */ + protected VariableBar(@NotNull Slot slot, int length, int height, @NotNull Priority priority) { + this(slot, length, height, priority, JavaPlugin.getProvidingPlugin(VariableBar.class)); + } + + protected VariableBar(int x, int y, int length, int height, @NotNull Priority priority) { + this(x, y, length, height, priority, JavaPlugin.getProvidingPlugin(VariableBar.class)); + } + + /** + * Creates a new variable bar + * + * @param slot the slot of the bar + * @param length the length of the bar + * @param height the height of the bar + * @since 0.10.8 + */ + protected VariableBar(@NotNull Slot slot, int length, int height) { + this(slot, length, height, Priority.NORMAL); } protected VariableBar(int x, int y, int length, int height) { @@ -184,6 +283,7 @@ public void setHeight(int height) { protected void applyContents(@NotNull VariableBar copy) { copy.x = x; copy.y = y; + copy.slot = slot; copy.length = length; copy.height = height; copy.setPriority(getPriority()); @@ -236,13 +336,20 @@ public void setOrientation(@NotNull Orientation orientation) { @Override public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength, int maxHeight) { - int newPaneOffsetX = paneOffsetX + getX(); - int newPaneOffsetY = paneOffsetY + getY(); + Slot slot = getSlot(); + + int newPaneOffsetX = paneOffsetX + slot.getX(maxLength); + int newPaneOffsetY = paneOffsetY + slot.getY(maxLength); int newMaxLength = Math.min(maxLength, getLength()); int newMaxHeight = Math.min(maxHeight, getHeight()); - this.backgroundPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, newMaxLength, newMaxHeight); - this.fillPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, newMaxLength, newMaxHeight); + if (this.backgroundPane.isVisible()) { + this.backgroundPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, newMaxLength, newMaxHeight); + } + + if (this.fillPane.isVisible()) { + this.fillPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, newMaxLength, newMaxHeight); + } } /** diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Mask.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Mask.java index 55bce832f..901d854a4 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Mask.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Mask.java @@ -154,7 +154,7 @@ public int amountOfEnabledSlots() { * @since 0.5.16 */ public boolean[] getColumn(int index) { - boolean[] column = new boolean[mask[0].length]; + boolean[] column = new boolean[mask.length]; for (int i = 0; i < getHeight(); i++) { column[i] = mask[i][index]; diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Slot.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Slot.java new file mode 100644 index 000000000..d1306fe42 --- /dev/null +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Slot.java @@ -0,0 +1,249 @@ +package com.github.stefvanschie.inventoryframework.pane.util; + +import com.github.stefvanschie.inventoryframework.exception.XMLLoadException; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.w3c.dom.Element; + +import java.util.Objects; + +/** + * A slot represents a position in some type of container. Implementors of this class represent slots in different ways. + * + * @since 0.10.8 + */ +public interface Slot { + + /** + * Gets the x coordinate of this slot. + * + * @param length the length of the parent container + * @return the x coordinate of this slot + * @since 0.10.8 + */ + @Contract(pure = true) + int getX(int length); + + /** + * Gets the y coordinate of this slot. + * + * @param length the length of the parent container + * @return the y coordinate of this slot + * @since 0.10.8 + */ + @Contract(pure = true) + int getY(int length); + + /** + * Deserializes the slot from an element. The slot may either be provided as an (x, y) coordinate pair via the "x" + * and "y" attributes; or as an index via the "index" attribute. If both forms are present, an + * {@link XMLLoadException} will be thrown. If only the "x" or the "y" attribute is present, but not both, an + * {@link XMLLoadException} will be thrown. If none of the aforementioned attributes appear, an + * {@link XMLLoadException} will be thrown. If any of these attributes contain a value that is not an integer, an + * {@link XMLLoadException} will be thrown. Otherwise, this will return a slot based on the present attributes. + * + * @param element the element from which to retrieve the attributes for the slot + * @return the deserialized slot + * @throws XMLLoadException if "x", "y", and "index" attributes are present; if only an "x" attribute is present; if + * only a "y" attribute is present; if no "x", "y", or "index" attribute is present; or if + * the "x", "y", or "index" attribute contain a value that is not an integer. + */ + @NotNull + @Contract(value = "_ -> new", pure = true) + static Slot deserialize(@NotNull Element element) { + boolean hasX = element.hasAttribute("x"); + boolean hasY = element.hasAttribute("y"); + boolean hasIndex = element.hasAttribute("index"); + + if (hasX && hasY && !hasIndex) { + int x, y; + + try { + x = Integer.parseInt(element.getAttribute("x")); + y = Integer.parseInt(element.getAttribute("y")); + } catch (NumberFormatException exception) { + throw new XMLLoadException("The x or y attribute does not have an integer as value"); + } + + return Slot.fromXY(x, y); + } + + if (hasIndex && !hasX && !hasY) { + int index; + + try { + index = Integer.parseInt(element.getAttribute("index")); + } catch (NumberFormatException exception) { + throw new XMLLoadException("The index attribute does not have an integer as value"); + } + + return Slot.fromIndex(index); + } + + throw new XMLLoadException("The combination of x, y and index attributes is invalid"); + } + + /** + * Creates a new slot based on an (x, y) coordinate pair. + * + * @param x the x coordinate + * @param y the y coordinate + * @return the slot representing this position + * @since 0.10.8 + */ + @NotNull + @Contract(value = "_, _ -> new", pure = true) + static Slot fromXY(int x, int y) { + return new XY(x, y); + } + + /** + * Creates a new slot based on an index. This index is relative to the parent container this slot will be used in. + * + * @param index the index + * @return the slot representing this relative position + * @since 0.10.8 + */ + @NotNull + @Contract("_ -> new") + static Slot fromIndex(int index) { + return new Indexed(index); + } + + /** + * A class representing a slot based on an (x, y) coordinate pair. + * + * @since 0.10.8 + */ + class XY implements Slot { + + /** + * The (x, y) coordinate pair + */ + private final int x, y; + + /** + * Creates a new slot based on an (x, y) coordinate pair. + * + * @param x the x coordinate + * @param y the y coordinate + * @since 0.10.8 + */ + private XY(int x, int y) { + this.x = x; + this.y = y; + } + + @Override + public int getX(int length) { + return this.x; + } + + @Override + public int getY(int length) { + return this.y; + } + + @Override + public boolean equals(@Nullable Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + XY xy = (XY) object; + + return x == xy.x && y == xy.y; + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } + } + + /** + * A class representing a slot based on an index. + * + * @since 0.10.8 + */ + class Indexed implements Slot { + + /** + * The index of this slot. + */ + private final int index; + + /** + * Creates a new slot based on an index. + * + * @param index the index of this slot + * @since 0.10.8 + */ + private Indexed(int index) { + this.index = index; + } + + /** + * {@inheritDoc} + * + * If {@code length} is zero, this will throw an {@link IllegalArgumentException}. + * + * @param length {@inheritDoc} + * @return {@inheritDoc} + * @throws IllegalArgumentException when {@code length} is zero + */ + @Override + @Contract(pure = true) + public int getX(int length) { + if (length == 0) { + throw new IllegalArgumentException("Length may not be zero"); + } + + return this.index % length; + } + + /** + * {@inheritDoc} + * + * If {@code length} is zero, this will throw an {@link IllegalArgumentException}. + * + * @param length {@inheritDoc} + * @return {@inheritDoc} + * @throws IllegalArgumentException when {@code length} is zero + */ + @Override + @Contract(pure = true) + public int getY(int length) { + if (length == 0) { + throw new IllegalArgumentException("Length may not be zero"); + } + + return this.index / length; + } + + @Override + public boolean equals(@Nullable Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + Indexed indexed = (Indexed) object; + + return index == indexed.index; + } + + @Override + public int hashCode() { + return index; + } + } +} diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/CSVUtil.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/CSVUtil.java index 1f2b75797..89d05da5f 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/CSVUtil.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/CSVUtil.java @@ -1,6 +1,5 @@ package com.github.stefvanschie.inventoryframework.util; -import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import java.io.BufferedReader; @@ -72,9 +71,7 @@ public static List readAll(@NotNull InputStream inputStream) throws IO array[i] = array[i].substring(1, array[i].length() - 1); } - array[i] = StringUtils.replace(array[i], "\"\"", "\""); - //Restore original code (array[i] = array[i].replace("\"\"", "\"")) - //once we update to Java 11, where it receives the current, faster implementation + array[i] = array[i].replace("\"\"", "\""); //replace unicode characters Matcher matcher = UNICODE_CHARACTER_PATTERN.matcher(array[i]); diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/InventoryViewUtil.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/InventoryViewUtil.java new file mode 100644 index 000000000..8d6327e69 --- /dev/null +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/InventoryViewUtil.java @@ -0,0 +1,42 @@ +package com.github.stefvanschie.inventoryframework.util; + +import com.github.stefvanschie.inventoryframework.inventoryview.abstraction.AbstractInventoryViewUtil; +import com.github.stefvanschie.inventoryframework.util.version.Version; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for working with {@link InventoryView}s across different definitions. + * + * @since 0.10.16 + */ +public class InventoryViewUtil { + + /** + * The underlying implementation. + */ + @Nullable + private static AbstractInventoryViewUtil IMPLEMENTATION; + + /** + * Gets the instance of this class to use for the current version. + * + * @return an instance of a utility class + * @since 0.10.16 + */ + @NotNull + @Contract(pure = true) + public static AbstractInventoryViewUtil getInstance() { + if (IMPLEMENTATION == null) { + if (Version.getVersion().isInventoryViewInterface()) { + IMPLEMENTATION = com.github.stefvanschie.inventoryframework.inventoryview.interface_.InventoryViewUtil.getInstance(); + } else { + IMPLEMENTATION = com.github.stefvanschie.inventoryframework.inventoryview.abstractclass.InventoryViewUtil.getInstance(); + } + } + + return IMPLEMENTATION; + } +} diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/SkullUtil.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/SkullUtil.java index b28b82bc4..1537767dc 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/SkullUtil.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/SkullUtil.java @@ -8,6 +8,8 @@ import org.jetbrains.annotations.NotNull; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Base64; import java.util.Objects; import java.util.UUID; @@ -51,17 +53,27 @@ public static ItemStack getSkull(@NotNull String id) { * @param id the skull id */ public static void setSkull(@NotNull ItemMeta meta, @NotNull String id) { - GameProfile profile = new GameProfile(UUID.randomUUID(), null); + GameProfile profile = new GameProfile(UUID.randomUUID(), ""); byte[] encodedData = Base64.getEncoder().encode(String.format("{textures:{SKIN:{url:\"%s\"}}}", "http://textures.minecraft.net/texture/" + id).getBytes()); profile.getProperties().put("textures", new Property("textures", new String(encodedData))); + String itemDisplayName = meta.getDisplayName(); try { Field profileField = meta.getClass().getDeclaredField("profile"); profileField.setAccessible(true); profileField.set(meta, profile); - } catch (NoSuchFieldException | SecurityException | IllegalAccessException e) { + + meta.setDisplayName(itemDisplayName); + + // Sets serializedProfile field on meta + // If it does throw NoSuchMethodException this stops, and meta is correct. + // Else it has profile and will set the field. + Method setProfile = meta.getClass().getDeclaredMethod("setProfile", GameProfile.class); + setProfile.setAccessible(true); + setProfile.invoke(meta, profile); + } catch (NoSuchFieldException | SecurityException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); - } + } catch (NoSuchMethodException ignored) {} } } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/TriFunction.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/TriFunction.java new file mode 100644 index 000000000..7ce718ca8 --- /dev/null +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/TriFunction.java @@ -0,0 +1,25 @@ +package com.github.stefvanschie.inventoryframework.util; + +/** + * A function that takes three arguments and returns a result. + * + * @param the type of the first argument + * @param the type of the second argument + * @param the type of the third argument + * @param the type of the result + * @since 0.10.8 + */ +@FunctionalInterface +public interface TriFunction { + + /** + * Applies this function to the given arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @return the result value + * @since 0.10.8 + */ + R apply(A a, B b, C c); +} diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/XMLUtil.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/XMLUtil.java index ede880dbc..1f3586bc9 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/XMLUtil.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/XMLUtil.java @@ -62,6 +62,30 @@ public static Consumer loadOnEventAttribute(@NotNull Object return null; } + /** + * Invokes the method by the given name on the given instance with the provided argument. The method should have + * the exact name specified and the exact parameter as specified. If the method cannot be accessed or found, this + * will throw an {@link XMLLoadException}. + * + * @param instance the instance on which to call the method + * @param methodName the name of the method to invoke + * @param argument the argument to provide for the invocation + * @param parameter the parameter of the method + * @since 0.10.3 + * @throws XMLLoadException if the method cannot be accessed or found + */ + public static void invokeMethod(@NotNull Object instance, @NotNull String methodName, @NotNull Object argument, + @NotNull Class parameter) { + try { + Method method = instance.getClass().getMethod(methodName, parameter); + + method.setAccessible(true); + method.invoke(instance, argument); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException exception) { + throw new XMLLoadException(exception); + } + } + /** * Sets a field from the given instance and element to the specified value * diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/Version.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/Version.java index aa2a2edca..93ddce445 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/Version.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/Version.java @@ -2,9 +2,13 @@ import com.github.stefvanschie.inventoryframework.exception.UnsupportedVersionException; import org.bukkit.Bukkit; +import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.EnumSet; + /** * The different supported NMS versions * @@ -13,39 +17,234 @@ public enum Version { /** - * Version 1.14 R1 + * Version 1.14 * - * @since 0.8.0 + * @since 0.10.0 */ - V1_14_R1, + V1_14, /** - * Version 1.15 R1 + * Version 1.15 * - * @since 0.8.0 + * @since 0.10.0 */ - V1_15_R1, + V1_15, /** - * Version 1.16 R1 + * Version 1.16.1 * - * @since 0.8.0 + * @since 0.10.0 */ - V1_16_R1, + V1_16_1, /** - * Version 1.16 R2 + * Version 1.16.2 - 1.16.3 * - * @since 0.8.0 + * @since 0.10.0 */ - V1_16_R2, + V1_16_2_3, /** - * Version 1.16 R3 + * Version 1.16.4 - 1.16.5 * - * @since 0.8.0 + * @since 0.10.0 + */ + V1_16_4_5, + + /** + * Version 1.17 + * + * @since 0.10.0 + */ + V1_17_0, + + /** + * Version 1.17.1 + * + * @since 0.10.0 + */ + V1_17_1, + + /** + * Version 1.18.0 + * + * @since 0.10.4 + */ + V1_18_0, + + /** + * Version 1.18.1 + * + * @since 0.10.4 + */ + V1_18_1, + + /** + * Version 1.18.2 + * + * @since 0.10.5 + */ + V1_18_2, + + /** + * Version 1.19.0 + * + * @since 0.10.6 + */ + V1_19_0, + + /** + * Version 1.19.1 + * + * @since 0.10.7 + */ + V1_19_1, + + /** + * Version 1.19.2 + * + * @since 0.10.7 + */ + V1_19_2, + + /** + * Version 1.19.3 + * + * @since 0.10.8 */ - V1_16_R3; + V1_19_3, + + /** + * Version 1.19.4 + * + * @since 0.10.9 + */ + V1_19_4, + + /** + * Version 1.20.0 + * + * @since 0.10.14 + */ + V1_20_0, + + /** + * Version 1.20.1 + * + * @since 0.10.14 + */ + V1_20_1, + + /** + * Version 1.20.2 + * + * @since 0.10.12 + */ + V1_20_2, + + /** + * Version 1.20.3 - 1.20.4 + * + * @since 0.10.13 + */ + V1_20_3_4, + + /** + * Version 1.20.5 + * + * @since 0.10.14 + */ + V1_20_5, + + /** + * Version 1.20.6 + * + * @since 0.10.14 + */ + V1_20_6, + + /** + * Version 1.21.0 + * + * @since 0.10.18 + */ + V1_21_0, + + /** + * Version 1.21.1 + * + * @since 0.10.18 + */ + V1_21_1, + + /** + * Version 1.21.2 - 1.21.3 + * + * @since 0.10.18 + */ + V1_21_2_3; + + /** + * A collection of versions on which modern smithing tables are available. + */ + private static final Collection MODERN_SMITHING_TABLE_VERSIONS = EnumSet.of( + V1_19_4, + V1_20_0, V1_20_1, V1_20_2, V1_20_3_4, V1_20_5, V1_20_6, + V1_21_0, V1_21_1, V1_21_2_3 + ); + + /** + * A collection of versions on which legacy smithing tables ae available. + */ + @NotNull + private static final Collection<@NotNull Version> LEGACY_SMITHING_TABLE_VERSIONS = EnumSet.of( + V1_14, + V1_15, + V1_16_1, V1_16_2_3, V1_16_4_5, + V1_17_0, V1_17_1, + V1_18_0, V1_18_1, V1_18_2, + V1_19_0, V1_19_1, V1_19_2, V1_19_3, V1_19_4 + ); + + /** + * A collection of versions on which {@link InventoryView} is an interface. + */ + @NotNull + private static final Collection<@NotNull Version> INTERFACE_INVENTORY_VIEW = EnumSet.of( + V1_21_0, V1_21_1, V1_21_2_3 + ); + + /** + * Checks whether the {@link InventoryView} class is an interface on this version. + * + * @return true if the class is an interface, false otherwise + * @since 0.10.16 + */ + @Contract(pure = true) + public boolean isInventoryViewInterface() { + return INTERFACE_INVENTORY_VIEW.contains(this); + } + + /** + * Checks whether modern smithing tables exist on this version. Returns true if they do, otherwise false. + * + * @return true if modern smithing tables are available + * @since 0.10.10 + */ + boolean existsModernSmithingTable() { + return MODERN_SMITHING_TABLE_VERSIONS.contains(this); + } + + /** + * Checks whether legacy smithing tables exist on this version. Returns true if they do, otherwise false. + * + * @return true if legacy smithing tables are available + * @since 0.10.10 + */ + @Contract(pure = true) + boolean existsLegacySmithingTable() { + return LEGACY_SMITHING_TABLE_VERSIONS.contains(this); + } /** * Gets the version currently being used. If the used version is not supported, an @@ -57,19 +256,67 @@ public enum Version { @NotNull @Contract(pure = true) public static Version getVersion() { - String version = Bukkit.getServer().getClass().getPackage().getName(); - - switch (version.substring(version.lastIndexOf('.') + 1)) { - case "v1_14_R1": - return V1_14_R1; - case "v1_15_R1": - return V1_15_R1; - case "v1_16_R1": - return V1_16_R1; - case "v1_16_R2": - return V1_16_R2; - case "v1_16_R3": - return V1_16_R3; + String version = Bukkit.getBukkitVersion().split("-")[0]; + + switch (version) { + case "1.14": + case "1.14.1": + case "1.14.2": + case "1.14.3": + case "1.14.4": + return V1_14; + case "1.15": + case "1.15.1": + case "1.15.2": + return V1_15; + case "1.16.1": + return V1_16_1; + case "1.16.2": + case "1.16.3": + return V1_16_2_3; + case "1.16.4": + case "1.16.5": + return V1_16_4_5; + case "1.17": + return V1_17_0; + case "1.17.1": + return V1_17_1; + case "1.18": + return V1_18_0; + case "1.18.1": + return V1_18_1; + case "1.18.2": + return V1_18_2; + case "1.19": + return V1_19_0; + case "1.19.1": + return V1_19_1; + case "1.19.2": + return V1_19_2; + case "1.19.3": + return V1_19_3; + case "1.19.4": + return V1_19_4; + case "1.20": + return V1_20_0; + case "1.20.1": + return V1_20_1; + case "1.20.2": + return V1_20_2; + case "1.20.3": + case "1.20.4": + return V1_20_3_4; + case "1.20.5": + return V1_20_5; + case "1.20.6": + return V1_20_6; + case "1.21": + return V1_21_0; + case "1.21.1": + return V1_21_1; + case "1.21.2": + case "1.21.3": + return V1_21_2_3; default: throw new UnsupportedVersionException("The server version provided is not supported"); } diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/VersionMatcher.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/VersionMatcher.java index c4f108e10..5ef836807 100644 --- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/VersionMatcher.java +++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/VersionMatcher.java @@ -2,6 +2,7 @@ import com.github.stefvanschie.inventoryframework.abstraction.*; import com.github.stefvanschie.inventoryframework.exception.UnsupportedVersionException; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.LegacySmithingTableInventoryImpl; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -41,11 +42,21 @@ public class VersionMatcher { */ private static final EnumMap> GRINDSTONE_INVENTORIES; + /** + * The different merchant inventories for different versions + */ + private static final EnumMap> MERCHANT_INVENTORIES; + /** * The different smithing table inventories for different versions */ private static final EnumMap> SMITHING_TABLE_INVENTORIES; + /** + * The different legacy smithing table inventories for different versions + */ + private static final EnumMap> LEGACY_SMITHING_TABLE_INVENTORIES; + /** * The different stonecutter inventories for different versions */ @@ -156,6 +167,24 @@ public static GrindstoneInventory newGrindstoneInventory(@NotNull Version versio } } + /** + * Gets a new merchant inventory for the specified version. + * + * @param version the version to get the inventory of + * @return the merchant inventory + * @since 0.10.1 + */ + @NotNull + @Contract(pure = true) + public static MerchantInventory newMerchantInventory(@NotNull Version version) { + try { + return MERCHANT_INVENTORIES.get(version).getConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException exception) { + throw new IllegalStateException(exception); + } + } + /** * Gets a new smithing table inventory for the specified version of the specified inventory holder. If a smithing * table is requested for a version that does not have smithing tables, an {@link UnsupportedVersionException} is @@ -164,17 +193,21 @@ public static GrindstoneInventory newGrindstoneInventory(@NotNull Version versio * @param version the version to get the inventory of * @param inventoryHolder the inventory holder * @return the smithing table inventory - * @since 0.8.0 + * @since 0.10.9 * @throws UnsupportedVersionException when a smithing table is requested on a version without smithing tables */ @NotNull @Contract(pure = true) - public static SmithingTableInventory newSmithingTableInventory(@NotNull Version version, - @NotNull InventoryHolder inventoryHolder) { - if (version == Version.V1_14_R1 || version == Version.V1_15_R1) { + public static SmithingTableInventory newModernSmithingTableInventory(@NotNull Version version, + @NotNull InventoryHolder inventoryHolder) { + if (!version.existsModernSmithingTable() && !version.existsLegacySmithingTable()) { throw new UnsupportedVersionException("Smithing tables didn't exist in version " + version); } + if (!version.existsModernSmithingTable()) { + throw new UnsupportedVersionException("Modern smithing tables didn't exist in version " + version); + } + try { Class clazz = SMITHING_TABLE_INVENTORIES.get(version); @@ -185,6 +218,39 @@ public static SmithingTableInventory newSmithingTableInventory(@NotNull Version } } + /** + * Gets a new legacy smithing table inventory for the specified version of the specified inventory holder. If a + * smithing table is requested for a version that does not have smithing tables, an + * {@link UnsupportedVersionException} is thrown. + * + * @param version the version to get the inventory of + * @param inventoryHolder the inventory holder + * @return the smithing table inventory + * @since 0.8.0 + * @throws UnsupportedVersionException when a smithing table is requested on a version without smithing tables + */ + @NotNull + @Contract(pure = true) + public static SmithingTableInventory newSmithingTableInventory(@NotNull Version version, + @NotNull InventoryHolder inventoryHolder) { + if (!version.existsModernSmithingTable() && !version.existsLegacySmithingTable()) { + throw new UnsupportedVersionException("Smithing tables didn't exist in version " + version); + } + + if (!version.existsLegacySmithingTable()) { + throw new UnsupportedVersionException("Legacy smithing tables don't exist in version " + version); + } + + try { + Class clazz = LEGACY_SMITHING_TABLE_INVENTORIES.get(version); + + return clazz.getConstructor(InventoryHolder.class).newInstance(inventoryHolder); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException exception) { + throw new IllegalStateException(exception); + } + } + /** * Gets a new stonecutter inventory for the specified version of the specified inventory holder. * @@ -209,83 +275,403 @@ public static StonecutterInventory newStonecutterInventory(@NotNull Version vers static { ANVIL_INVENTORIES = new EnumMap<>(Version.class); - ANVIL_INVENTORIES.put(Version.V1_14_R1, - com.github.stefvanschie.inventoryframework.nms.v1_14_R1.AnvilInventoryImpl.class); - ANVIL_INVENTORIES.put(Version.V1_15_R1, - com.github.stefvanschie.inventoryframework.nms.v1_15_R1.AnvilInventoryImpl.class); - ANVIL_INVENTORIES.put(Version.V1_16_R1, - com.github.stefvanschie.inventoryframework.nms.v1_16_R1.AnvilInventoryImpl.class); - ANVIL_INVENTORIES.put(Version.V1_16_R2, - com.github.stefvanschie.inventoryframework.nms.v1_16_R2.AnvilInventoryImpl.class); - ANVIL_INVENTORIES.put(Version.V1_16_R3, - com.github.stefvanschie.inventoryframework.nms.v1_16_R3.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_14, + com.github.stefvanschie.inventoryframework.nms.v1_14.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_15, + com.github.stefvanschie.inventoryframework.nms.v1_15.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_16_1, + com.github.stefvanschie.inventoryframework.nms.v1_16_1.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_16_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_16_4_5, + com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_17_0, + com.github.stefvanschie.inventoryframework.nms.v1_17_0.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_17_1, + com.github.stefvanschie.inventoryframework.nms.v1_17_1.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_18_0, + com.github.stefvanschie.inventoryframework.nms.v1_18_0.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_18_1, + com.github.stefvanschie.inventoryframework.nms.v1_18_1.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_18_2, + com.github.stefvanschie.inventoryframework.nms.v1_18_2.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_19_0, + com.github.stefvanschie.inventoryframework.nms.v1_19_0.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_19_1, + com.github.stefvanschie.inventoryframework.nms.v1_19_1.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_19_2, + com.github.stefvanschie.inventoryframework.nms.v1_19_2.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_19_3, + com.github.stefvanschie.inventoryframework.nms.v1_19_3.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_19_4, + com.github.stefvanschie.inventoryframework.nms.v1_19_4.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_20_0, + com.github.stefvanschie.inventoryframework.nms.v1_20_0.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_20_1, + com.github.stefvanschie.inventoryframework.nms.v1_20_1.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_20_2, + com.github.stefvanschie.inventoryframework.nms.v1_20_2.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_20_3_4, + com.github.stefvanschie.inventoryframework.nms.v1_20_3.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_20_5, + com.github.stefvanschie.inventoryframework.nms.v1_20_5.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_20_6, + com.github.stefvanschie.inventoryframework.nms.v1_20_6.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_21_0, + com.github.stefvanschie.inventoryframework.nms.v1_21_0.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_21_1, + com.github.stefvanschie.inventoryframework.nms.v1_21_1.AnvilInventoryImpl.class); + ANVIL_INVENTORIES.put(Version.V1_21_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.AnvilInventoryImpl.class); BEACON_INVENTORIES = new EnumMap<>(Version.class); - BEACON_INVENTORIES.put(Version.V1_14_R1, - com.github.stefvanschie.inventoryframework.nms.v1_14_R1.BeaconInventoryImpl.class); - BEACON_INVENTORIES.put(Version.V1_15_R1, - com.github.stefvanschie.inventoryframework.nms.v1_15_R1.BeaconInventoryImpl.class); - BEACON_INVENTORIES.put(Version.V1_16_R1, - com.github.stefvanschie.inventoryframework.nms.v1_16_R1.BeaconInventoryImpl.class); - BEACON_INVENTORIES.put(Version.V1_16_R2, - com.github.stefvanschie.inventoryframework.nms.v1_16_R2.BeaconInventoryImpl.class); - BEACON_INVENTORIES.put(Version.V1_16_R3, - com.github.stefvanschie.inventoryframework.nms.v1_16_R3.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_14, + com.github.stefvanschie.inventoryframework.nms.v1_14.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_15, + com.github.stefvanschie.inventoryframework.nms.v1_15.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_16_1, + com.github.stefvanschie.inventoryframework.nms.v1_16_1.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_16_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_16_4_5, + com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_17_0, + com.github.stefvanschie.inventoryframework.nms.v1_17_0.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_17_1, + com.github.stefvanschie.inventoryframework.nms.v1_17_1.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_18_0, + com.github.stefvanschie.inventoryframework.nms.v1_18_0.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_18_1, + com.github.stefvanschie.inventoryframework.nms.v1_18_1.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_18_2, + com.github.stefvanschie.inventoryframework.nms.v1_18_2.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_19_0, + com.github.stefvanschie.inventoryframework.nms.v1_19_0.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_19_1, + com.github.stefvanschie.inventoryframework.nms.v1_19_1.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_19_2, + com.github.stefvanschie.inventoryframework.nms.v1_19_2.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_19_3, + com.github.stefvanschie.inventoryframework.nms.v1_19_3.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_19_4, + com.github.stefvanschie.inventoryframework.nms.v1_19_4.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_20_0, + com.github.stefvanschie.inventoryframework.nms.v1_20_0.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_20_1, + com.github.stefvanschie.inventoryframework.nms.v1_20_1.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_20_2, + com.github.stefvanschie.inventoryframework.nms.v1_20_2.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_20_3_4, + com.github.stefvanschie.inventoryframework.nms.v1_20_3.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_20_5, + com.github.stefvanschie.inventoryframework.nms.v1_20_5.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_20_6, + com.github.stefvanschie.inventoryframework.nms.v1_20_6.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_21_0, + com.github.stefvanschie.inventoryframework.nms.v1_21_0.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_21_1, + com.github.stefvanschie.inventoryframework.nms.v1_21_1.BeaconInventoryImpl.class); + BEACON_INVENTORIES.put(Version.V1_21_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.BeaconInventoryImpl.class); CARTOGRAPHY_TABLE_INVENTORIES = new EnumMap<>(Version.class); - CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_14_R1, - com.github.stefvanschie.inventoryframework.nms.v1_14_R1.CartographyTableInventoryImpl.class); - CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_15_R1, - com.github.stefvanschie.inventoryframework.nms.v1_15_R1.CartographyTableInventoryImpl.class); - CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_16_R1, - com.github.stefvanschie.inventoryframework.nms.v1_16_R1.CartographyTableInventoryImpl.class); - CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_16_R2, - com.github.stefvanschie.inventoryframework.nms.v1_16_R2.CartographyTableInventoryImpl.class); - CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_16_R3, - com.github.stefvanschie.inventoryframework.nms.v1_16_R3.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_14, + com.github.stefvanschie.inventoryframework.nms.v1_14.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_15, + com.github.stefvanschie.inventoryframework.nms.v1_15.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_16_1, + com.github.stefvanschie.inventoryframework.nms.v1_16_1.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_16_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_16_4_5, + com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_17_0, + com.github.stefvanschie.inventoryframework.nms.v1_17_0.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_17_1, + com.github.stefvanschie.inventoryframework.nms.v1_17_1.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_18_0, + com.github.stefvanschie.inventoryframework.nms.v1_18_0.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_18_1, + com.github.stefvanschie.inventoryframework.nms.v1_18_1.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_18_2, + com.github.stefvanschie.inventoryframework.nms.v1_18_2.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_19_0, + com.github.stefvanschie.inventoryframework.nms.v1_19_0.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_19_1, + com.github.stefvanschie.inventoryframework.nms.v1_19_1.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_19_2, + com.github.stefvanschie.inventoryframework.nms.v1_19_2.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_19_3, + com.github.stefvanschie.inventoryframework.nms.v1_19_3.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_19_4, + com.github.stefvanschie.inventoryframework.nms.v1_19_4.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_0, + com.github.stefvanschie.inventoryframework.nms.v1_20_0.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_1, + com.github.stefvanschie.inventoryframework.nms.v1_20_1.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_2, + com.github.stefvanschie.inventoryframework.nms.v1_20_2.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_3_4, + com.github.stefvanschie.inventoryframework.nms.v1_20_3.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_5, + com.github.stefvanschie.inventoryframework.nms.v1_20_5.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_6, + com.github.stefvanschie.inventoryframework.nms.v1_20_6.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_21_0, + com.github.stefvanschie.inventoryframework.nms.v1_21_0.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_21_1, + com.github.stefvanschie.inventoryframework.nms.v1_21_1.CartographyTableInventoryImpl.class); + CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_21_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.CartographyTableInventoryImpl.class); ENCHANTING_TABLE_INVENTORIES = new EnumMap<>(Version.class); - ENCHANTING_TABLE_INVENTORIES.put(Version.V1_14_R1, - com.github.stefvanschie.inventoryframework.nms.v1_14_R1.EnchantingTableInventoryImpl.class); - ENCHANTING_TABLE_INVENTORIES.put(Version.V1_15_R1, - com.github.stefvanschie.inventoryframework.nms.v1_15_R1.EnchantingTableInventoryImpl.class); - ENCHANTING_TABLE_INVENTORIES.put(Version.V1_16_R1, - com.github.stefvanschie.inventoryframework.nms.v1_16_R1.EnchantingTableInventoryImpl.class); - ENCHANTING_TABLE_INVENTORIES.put(Version.V1_16_R2, - com.github.stefvanschie.inventoryframework.nms.v1_16_R2.EnchantingTableInventoryImpl.class); - ENCHANTING_TABLE_INVENTORIES.put(Version.V1_16_R3, - com.github.stefvanschie.inventoryframework.nms.v1_16_R3.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_14, + com.github.stefvanschie.inventoryframework.nms.v1_14.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_15, + com.github.stefvanschie.inventoryframework.nms.v1_15.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_16_1, + com.github.stefvanschie.inventoryframework.nms.v1_16_1.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_16_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_16_4_5, + com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_17_0, + com.github.stefvanschie.inventoryframework.nms.v1_17_0.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_17_1, + com.github.stefvanschie.inventoryframework.nms.v1_17_1.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_18_0, + com.github.stefvanschie.inventoryframework.nms.v1_18_0.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_18_1, + com.github.stefvanschie.inventoryframework.nms.v1_18_1.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_18_2, + com.github.stefvanschie.inventoryframework.nms.v1_18_2.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_19_0, + com.github.stefvanschie.inventoryframework.nms.v1_19_0.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_19_1, + com.github.stefvanschie.inventoryframework.nms.v1_19_1.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_19_2, + com.github.stefvanschie.inventoryframework.nms.v1_19_2.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_19_3, + com.github.stefvanschie.inventoryframework.nms.v1_19_3.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_19_4, + com.github.stefvanschie.inventoryframework.nms.v1_19_4.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_0, + com.github.stefvanschie.inventoryframework.nms.v1_20_0.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_1, + com.github.stefvanschie.inventoryframework.nms.v1_20_1.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_2, + com.github.stefvanschie.inventoryframework.nms.v1_20_2.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_3_4, + com.github.stefvanschie.inventoryframework.nms.v1_20_3.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_5, + com.github.stefvanschie.inventoryframework.nms.v1_20_5.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_6, + com.github.stefvanschie.inventoryframework.nms.v1_20_6.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_21_0, + com.github.stefvanschie.inventoryframework.nms.v1_21_0.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_21_1, + com.github.stefvanschie.inventoryframework.nms.v1_21_1.EnchantingTableInventoryImpl.class); + ENCHANTING_TABLE_INVENTORIES.put(Version.V1_21_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.EnchantingTableInventoryImpl.class); GRINDSTONE_INVENTORIES = new EnumMap<>(Version.class); - GRINDSTONE_INVENTORIES.put(Version.V1_14_R1, - com.github.stefvanschie.inventoryframework.nms.v1_14_R1.GrindstoneInventoryImpl.class); - GRINDSTONE_INVENTORIES.put(Version.V1_15_R1, - com.github.stefvanschie.inventoryframework.nms.v1_15_R1.GrindstoneInventoryImpl.class); - GRINDSTONE_INVENTORIES.put(Version.V1_16_R1, - com.github.stefvanschie.inventoryframework.nms.v1_16_R1.GrindstoneInventoryImpl.class); - GRINDSTONE_INVENTORIES.put(Version.V1_16_R2, - com.github.stefvanschie.inventoryframework.nms.v1_16_R2.GrindstoneInventoryImpl.class); - GRINDSTONE_INVENTORIES.put(Version.V1_16_R3, - com.github.stefvanschie.inventoryframework.nms.v1_16_R3.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_14, + com.github.stefvanschie.inventoryframework.nms.v1_14.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_15, + com.github.stefvanschie.inventoryframework.nms.v1_15.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_16_1, + com.github.stefvanschie.inventoryframework.nms.v1_16_1.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_16_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_16_4_5, + com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_17_0, + com.github.stefvanschie.inventoryframework.nms.v1_17_0.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_17_1, + com.github.stefvanschie.inventoryframework.nms.v1_17_1.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_18_0, + com.github.stefvanschie.inventoryframework.nms.v1_18_0.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_18_1, + com.github.stefvanschie.inventoryframework.nms.v1_18_1.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_18_2, + com.github.stefvanschie.inventoryframework.nms.v1_18_2.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_19_0, + com.github.stefvanschie.inventoryframework.nms.v1_19_0.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_19_1, + com.github.stefvanschie.inventoryframework.nms.v1_19_1.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_19_2, + com.github.stefvanschie.inventoryframework.nms.v1_19_2.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_19_3, + com.github.stefvanschie.inventoryframework.nms.v1_19_3.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_19_4, + com.github.stefvanschie.inventoryframework.nms.v1_19_4.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_20_0, + com.github.stefvanschie.inventoryframework.nms.v1_20_0.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_20_1, + com.github.stefvanschie.inventoryframework.nms.v1_20_1.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_20_2, + com.github.stefvanschie.inventoryframework.nms.v1_20_2.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_20_3_4, + com.github.stefvanschie.inventoryframework.nms.v1_20_3.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_20_5, + com.github.stefvanschie.inventoryframework.nms.v1_20_5.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_20_6, + com.github.stefvanschie.inventoryframework.nms.v1_20_6.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_21_0, + com.github.stefvanschie.inventoryframework.nms.v1_21_0.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_21_1, + com.github.stefvanschie.inventoryframework.nms.v1_21_1.GrindstoneInventoryImpl.class); + GRINDSTONE_INVENTORIES.put(Version.V1_21_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.GrindstoneInventoryImpl.class); + + MERCHANT_INVENTORIES = new EnumMap<>(Version.class); + MERCHANT_INVENTORIES.put(Version.V1_14, + com.github.stefvanschie.inventoryframework.nms.v1_14.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_15, + com.github.stefvanschie.inventoryframework.nms.v1_15.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_16_1, + com.github.stefvanschie.inventoryframework.nms.v1_16_1.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_16_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_16_4_5, + com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_17_0, + com.github.stefvanschie.inventoryframework.nms.v1_17_0.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_17_1, + com.github.stefvanschie.inventoryframework.nms.v1_17_1.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_18_0, + com.github.stefvanschie.inventoryframework.nms.v1_18_0.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_18_1, + com.github.stefvanschie.inventoryframework.nms.v1_18_1.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_18_2, + com.github.stefvanschie.inventoryframework.nms.v1_18_2.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_19_0, + com.github.stefvanschie.inventoryframework.nms.v1_19_0.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_19_1, + com.github.stefvanschie.inventoryframework.nms.v1_19_1.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_19_2, + com.github.stefvanschie.inventoryframework.nms.v1_19_2.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_19_3, + com.github.stefvanschie.inventoryframework.nms.v1_19_3.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_19_4, + com.github.stefvanschie.inventoryframework.nms.v1_19_4.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_20_0, + com.github.stefvanschie.inventoryframework.nms.v1_20_0.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_20_1, + com.github.stefvanschie.inventoryframework.nms.v1_20_1.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_20_2, + com.github.stefvanschie.inventoryframework.nms.v1_20_2.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_20_3_4, + com.github.stefvanschie.inventoryframework.nms.v1_20_3.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_20_5, + com.github.stefvanschie.inventoryframework.nms.v1_20_5.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_20_6, + com.github.stefvanschie.inventoryframework.nms.v1_20_6.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_21_0, + com.github.stefvanschie.inventoryframework.nms.v1_21_0.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_21_1, + com.github.stefvanschie.inventoryframework.nms.v1_21_1.MerchantInventoryImpl.class); + MERCHANT_INVENTORIES.put(Version.V1_21_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.MerchantInventoryImpl.class); SMITHING_TABLE_INVENTORIES = new EnumMap<>(Version.class); - SMITHING_TABLE_INVENTORIES.put(Version.V1_16_R1, - com.github.stefvanschie.inventoryframework.nms.v1_16_R1.SmithingTableInventoryImpl.class); - SMITHING_TABLE_INVENTORIES.put(Version.V1_16_R2, - com.github.stefvanschie.inventoryframework.nms.v1_16_R2.SmithingTableInventoryImpl.class); - SMITHING_TABLE_INVENTORIES.put(Version.V1_16_R3, - com.github.stefvanschie.inventoryframework.nms.v1_16_R3.SmithingTableInventoryImpl.class); + SMITHING_TABLE_INVENTORIES.put(Version.V1_19_4, + com.github.stefvanschie.inventoryframework.nms.v1_19_4.SmithingTableInventoryImpl.class); + SMITHING_TABLE_INVENTORIES.put(Version.V1_20_0, + com.github.stefvanschie.inventoryframework.nms.v1_20_0.SmithingTableInventoryImpl.class); + SMITHING_TABLE_INVENTORIES.put(Version.V1_20_1, + com.github.stefvanschie.inventoryframework.nms.v1_20_1.SmithingTableInventoryImpl.class); + SMITHING_TABLE_INVENTORIES.put(Version.V1_20_2, + com.github.stefvanschie.inventoryframework.nms.v1_20_2.SmithingTableInventoryImpl.class); + SMITHING_TABLE_INVENTORIES.put(Version.V1_20_3_4, + com.github.stefvanschie.inventoryframework.nms.v1_20_3.SmithingTableInventoryImpl.class); + SMITHING_TABLE_INVENTORIES.put(Version.V1_20_5, + com.github.stefvanschie.inventoryframework.nms.v1_20_5.SmithingTableInventoryImpl.class); + SMITHING_TABLE_INVENTORIES.put(Version.V1_20_6, + com.github.stefvanschie.inventoryframework.nms.v1_20_6.SmithingTableInventoryImpl.class); + SMITHING_TABLE_INVENTORIES.put(Version.V1_21_0, + com.github.stefvanschie.inventoryframework.nms.v1_21_0.SmithingTableInventoryImpl.class); + SMITHING_TABLE_INVENTORIES.put(Version.V1_21_1, + com.github.stefvanschie.inventoryframework.nms.v1_21_1.SmithingTableInventoryImpl.class); + SMITHING_TABLE_INVENTORIES.put(Version.V1_21_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.SmithingTableInventoryImpl.class); + + LEGACY_SMITHING_TABLE_INVENTORIES = new EnumMap<>(Version.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_16_1, + com.github.stefvanschie.inventoryframework.nms.v1_16_1.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_16_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_16_4_5, + com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_17_0, + com.github.stefvanschie.inventoryframework.nms.v1_17_0.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_17_1, + com.github.stefvanschie.inventoryframework.nms.v1_17_1.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_18_0, + com.github.stefvanschie.inventoryframework.nms.v1_18_0.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_18_1, + com.github.stefvanschie.inventoryframework.nms.v1_18_1.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_18_2, + com.github.stefvanschie.inventoryframework.nms.v1_18_2.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_19_0, + com.github.stefvanschie.inventoryframework.nms.v1_19_0.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_19_1, + com.github.stefvanschie.inventoryframework.nms.v1_19_1.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_19_2, + com.github.stefvanschie.inventoryframework.nms.v1_19_2.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_19_3, + com.github.stefvanschie.inventoryframework.nms.v1_19_3.SmithingTableInventoryImpl.class); + LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_19_4, + LegacySmithingTableInventoryImpl.class); STONECUTTER_INVENTORIES = new EnumMap<>(Version.class); - STONECUTTER_INVENTORIES.put(Version.V1_14_R1, - com.github.stefvanschie.inventoryframework.nms.v1_14_R1.StonecutterInventoryImpl.class); - STONECUTTER_INVENTORIES.put(Version.V1_15_R1, - com.github.stefvanschie.inventoryframework.nms.v1_15_R1.StonecutterInventoryImpl.class); - STONECUTTER_INVENTORIES.put(Version.V1_16_R1, - com.github.stefvanschie.inventoryframework.nms.v1_16_R1.StonecutterInventoryImpl.class); - STONECUTTER_INVENTORIES.put(Version.V1_16_R2, - com.github.stefvanschie.inventoryframework.nms.v1_16_R2.StonecutterInventoryImpl.class); - STONECUTTER_INVENTORIES.put(Version.V1_16_R3, - com.github.stefvanschie.inventoryframework.nms.v1_16_R3.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_14, + com.github.stefvanschie.inventoryframework.nms.v1_14.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_15, + com.github.stefvanschie.inventoryframework.nms.v1_15.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_16_1, + com.github.stefvanschie.inventoryframework.nms.v1_16_1.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_16_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_16_4_5, + com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_17_0, + com.github.stefvanschie.inventoryframework.nms.v1_17_0.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_17_1, + com.github.stefvanschie.inventoryframework.nms.v1_17_1.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_18_0, + com.github.stefvanschie.inventoryframework.nms.v1_18_0.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_18_1, + com.github.stefvanschie.inventoryframework.nms.v1_18_1.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_18_2, + com.github.stefvanschie.inventoryframework.nms.v1_18_2.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_19_0, + com.github.stefvanschie.inventoryframework.nms.v1_19_0.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_19_1, + com.github.stefvanschie.inventoryframework.nms.v1_19_1.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_19_2, + com.github.stefvanschie.inventoryframework.nms.v1_19_2.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_19_3, + com.github.stefvanschie.inventoryframework.nms.v1_19_3.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_19_4, + com.github.stefvanschie.inventoryframework.nms.v1_19_4.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_20_0, + com.github.stefvanschie.inventoryframework.nms.v1_20_0.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_20_1, + com.github.stefvanschie.inventoryframework.nms.v1_20_1.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_20_2, + com.github.stefvanschie.inventoryframework.nms.v1_20_2.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_20_3_4, + com.github.stefvanschie.inventoryframework.nms.v1_20_3.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_20_5, + com.github.stefvanschie.inventoryframework.nms.v1_20_5.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_20_6, + com.github.stefvanschie.inventoryframework.nms.v1_20_6.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_21_0, + com.github.stefvanschie.inventoryframework.nms.v1_21_0.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_21_1, + com.github.stefvanschie.inventoryframework.nms.v1_21_1.StonecutterInventoryImpl.class); + STONECUTTER_INVENTORIES.put(Version.V1_21_2_3, + com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.StonecutterInventoryImpl.class); } } diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/OutlinePaneTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/OutlinePaneTest.java index 3522c1185..29f0a4411 100644 --- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/OutlinePaneTest.java +++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/OutlinePaneTest.java @@ -24,6 +24,7 @@ void testCopy() { original.flipHorizontally(true); original.flipVertically(true); original.applyMask(new Mask("0")); + original.align(OutlinePane.Alignment.CENTER); OutlinePane copy = original.copy(); @@ -42,6 +43,7 @@ void testCopy() { assertEquals(original.isFlippedHorizontally(), copy.isFlippedHorizontally()); assertEquals(original.isFlippedVertically(), copy.isFlippedVertically()); assertEquals(original.getMask(), copy.getMask()); + assertEquals(original.getAlignment(), copy.getAlignment()); assertEquals(original.getUUID(), copy.getUUID()); } } diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPaneTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPaneTest.java index 6be43125d..af2c6b54e 100644 --- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPaneTest.java +++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPaneTest.java @@ -1,15 +1,60 @@ package com.github.stefvanschie.inventoryframework.pane; -import com.github.stefvanschie.inventoryframework.font.util.Font; -import com.github.stefvanschie.inventoryframework.pane.component.Label; -import com.github.stefvanschie.inventoryframework.pane.component.PercentageBar; -import com.github.stefvanschie.inventoryframework.pane.component.ToggleButton; import org.junit.jupiter.api.Test; +import java.util.Collection; + import static org.junit.jupiter.api.Assertions.*; public class PaginatedPaneTest { + @Test + void testAddPageEmpty() { + PaginatedPane paginatedPane = new PaginatedPane(0, 0, 1, 1); + + StaticPane staticPane = new StaticPane(0, 0, 1, 1); + + assertDoesNotThrow(() -> { + paginatedPane.addPage(staticPane); + + Collection panes = paginatedPane.getPanes(0); + + assertEquals(1, panes.size()); + assertSame(staticPane, panes.iterator().next()); + }); + } + + @Test + void testAddPageNotEmpty() { + PaginatedPane paginatedPane = new PaginatedPane(0, 0, 1, 1); + + StaticPane staticPane1 = new StaticPane(0, 0, 1, 1); + StaticPane staticPane2 = new StaticPane(0, 0, 1, 1); + + paginatedPane.addPane(0, staticPane1); + + assertDoesNotThrow(() -> { + paginatedPane.addPage(staticPane2); + + Collection panes = paginatedPane.getPanes(1); + + assertEquals(1, panes.size()); + assertSame(staticPane2, panes.iterator().next()); + }); + } + + @Test + void testAddPageException() { + PaginatedPane paginatedPane = new PaginatedPane(0, 0, 1, 1); + + StaticPane staticPane1 = new StaticPane(0, 0, 1, 1); + StaticPane staticPane2 = new StaticPane(0, 0, 1, 1); + + paginatedPane.addPane(Integer.MAX_VALUE, staticPane1); + + assertThrows(ArithmeticException.class, () -> paginatedPane.addPage(staticPane2)); + } + @Test void testCopy() { PaginatedPane original = new PaginatedPane(5, 5, 4, 1, Pane.Priority.NORMAL); @@ -37,4 +82,37 @@ void testCopy() { assertEquals(original.getPages(), copy.getPages()); assertEquals(original.getUUID(), copy.getUUID()); } + + @Test + void testDeletePageExists() { + PaginatedPane pane = new PaginatedPane(0, 0, 1, 1); + + StaticPane staticPane = new StaticPane(0, 0, 1, 1); + + pane.addPane(0, new StaticPane(0, 0, 1, 1)); + pane.addPane(1, staticPane); + + pane.deletePage(0); + + assertEquals(1, pane.getPages()); + assertEquals(1, pane.getPanes(0).size()); + assertSame(staticPane, pane.getPanes(0).toArray(new Pane[0])[0]); + } + + @Test + void testDeletePageNotExists() { + PaginatedPane pane = new PaginatedPane(0, 0, 1, 1); + + StaticPane staticPane1 = new StaticPane(0, 0, 1, 1); + StaticPane staticPane2 = new StaticPane(0, 0, 1, 1); + + pane.addPane(0, staticPane1); + pane.addPane(1, staticPane2); + + pane.deletePage(2); + + assertEquals(2, pane.getPages()); + assertSame(staticPane1, pane.getPanes(0).toArray(new Pane[0])[0]); + assertSame(staticPane2, pane.getPanes(1).toArray(new Pane[0])[0]); + } } diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/MaskTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/MaskTest.java index 3545e05ab..189af67ef 100644 --- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/MaskTest.java +++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/MaskTest.java @@ -39,6 +39,11 @@ void testGetColumn() { "10", "00" ).getColumn(0)); + + assertArrayEquals(new boolean[] {true, false}, new Mask( + "1", + "0" + ).getColumn(0)); } @Test diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/SlotTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/SlotTest.java new file mode 100644 index 000000000..2ad037ef8 --- /dev/null +++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/SlotTest.java @@ -0,0 +1,23 @@ +package com.github.stefvanschie.inventoryframework.pane.util; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class SlotTest { + + @Test + void testEquals() { + assertEquals(Slot.fromXY(0, 0), Slot.fromXY(0, 0)); + assertNotEquals(Slot.fromXY(0, 1), Slot.fromXY(1, 0)); + + assertEquals(Slot.fromIndex(0), Slot.fromIndex(0)); + assertNotEquals(Slot.fromIndex(0), Slot.fromIndex(1)); + } + + @Test + void testHashCode() { + assertEquals(Slot.fromXY(0, 0).hashCode(), Slot.fromXY(0, 0).hashCode()); + assertEquals(Slot.fromIndex(0).hashCode(), Slot.fromIndex(0).hashCode()); + } +} diff --git a/README.md b/README.md index a6d6353e1..666872443 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # IF Discord guild -*This framework works for Minecraft versions 1.14-1.16* +*This framework works for Minecraft versions 1.14-1.21* An inventory framework for managing GUIs @@ -14,7 +14,7 @@ To add this project as a dependency to your pom.xml, add the following to your p com.github.stefvanschie.inventoryframework IF - 0.9.8 + 0.10.18 ``` The project is in the Central Repository, so specifying a repository is not needed. @@ -24,7 +24,7 @@ Now in order to shade the project into your project, add the following to your p org.apache.maven.plugins maven-shade-plugin - 3.2.2 + 3.5.2 ${project.build.directory}/dependency-reduced-pom.xml @@ -50,7 +50,7 @@ Replace [YOUR PACKAGE] with the top-level package of your project. To add this project as a dependency for your Gradle project, make sure your `dependencies` section of your build.gradle looks like the following: ```Groovy dependencies { - compile 'com.github.stefvanschie.inventoryframework:IF:0.9.8' + implementation 'com.github.stefvanschie.inventoryframework:IF:0.10.18' // ... } ``` @@ -63,7 +63,10 @@ repositories { ``` In order to include the project in your own project, you will need to use the `shadowJar` plugin. If you don't have it already, add the following to the top of your file: ```Groovy -apply plugin: 'com.github.johnrengelman.shadow' +plugins { + // ... + id "com.github.johnrengelman.shadow" version "7.1.2" +} ``` To relocate the project's classes to your own namespace, add the following, with [YOUR PACKAGE] being the top-level package of your project: ```Groovy @@ -72,11 +75,162 @@ shadowJar { } ``` +## Dependency via plugin.yml +IF does **not** support declaring the dependency via the libraries section in the plugin.yml. Please make use of a build tool as described above to use IF as a dependency. + ## Building from source -If you want to build this project from source, run the following from Git Bash: +If you want to build this project from source, run the following: git clone https://github.com/stefvanschie/IF.git - cd IF - mvn clean package -The build can then be found in /IF/target/. +This will clone this repository to your device. This project relies on NMS, for which the dependencies are not available online. Because of this, you'll need to follow additional steps to obtain all these dependencies locally. + +### Installing Paper manually +For versions 1.14-1.16, we have to manually install Paper. Run the following scripts for each version to install the dependencies locally. Running these commands generate additional files in the folder where you execute them. To ensure that you don't accidentallly overwrite other files, execute this in an empty folder. The files that get created can be deleted afterwards (either after installing a single version or after installing all of them), since they're no longer necessary. + +#### 1.14.4 +``` +wget https://papermc.io/api/v2/projects/paper/versions/1.14.4/builds/243/downloads/paper-1.14.4-243.jar -O paperclip/paper-1.14.4.jar +java -jar paper-1.14.4.jar +mvn install:install-file -Dfile=cache/patched_1.14.4.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.14.4-R0.1-SNAPSHOT" -Dpackaging="jar" +``` + +#### 1.15.2 +``` +wget https://papermc.io/api/v2/projects/paper/versions/1.15.2/builds/391/downloads/paper-1.15.2-391.jar -O paperclip/paper-1.15.2.jar +java -jar paper-1.15.2.jar +mvn install:install-file -Dfile=cache/patched_1.15.2.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.15.2-R0.1-SNAPSHOT" -Dpackaging="jar" +``` + +#### 1.16.1 +``` +wget https://papermc.io/api/v2/projects/paper/versions/1.16.1/builds/138/downloads/paper-1.16.1-138.jar -O paperclip/paper-1.16.1.jar +java -jar paper-1.16.1.jar +mvn install:install-file -Dfile=cache/patched_1.16.1.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.1-R0.1-SNAPSHOT" -Dpackaging="jar" +``` + +#### 1.16.3 +``` +wget https://papermc.io/api/v2/projects/paper/versions/1.16.3/builds/253/downloads/paper-1.16.3-253.jar -O paperclip/paper-1.16.3.jar +java -jar paper-1.16.3.jar +mvn install:install-file -Dfile=cache/patched_1.16.3.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.3-R0.1-SNAPSHOT" -Dpackaging="jar" +``` + +#### 1.16.4 +``` +wget https://papermc.io/api/v2/projects/paper/versions/1.16.4/builds/416/downloads/paper-1.16.4-416.jar -O paperclip/paper-1.16.4.jar +java -jar paper-1.16.4.jar +mvn install:install-file -Dfile=cache/patched_1.16.4.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.4-R0.1-SNAPSHOT" -Dpackaging="jar" +``` + +### Installing Paper via the maven plugin +For versions 1.17-1.20.4, we use Paper via the [paper-nms-maven-plugin](https://github.com/Alvinn8/paper-nms-maven-plugin). To install these versions locally, we must run a few maven commands. These commands should be ran in the root directory of the project. +``` +mvn paper-nms:init -pl nms/1_17_0 +mvn paper-nms:init -pl nms/1_17_1 +mvn paper-nms:init -pl nms/1_18_0 +mvn paper-nms:init -pl nms/1_18_1 +mvn paper-nms:init -pl nms/1_18_2 +mvn paper-nms:init -pl nms/1_19_0 +mvn paper-nms:init -pl nms/1_19_1 +mvn paper-nms:init -pl nms/1_19_2 +mvn paper-nms:init -pl nms/1_19_3 +mvn paper-nms:init -pl nms/1_19_4 +mvn paper-nms:init -pl nms/1_20_0-1 +mvn paper-nms:init -pl nms/1_20_2 +mvn paper-nms:init -pl nms/1_20_3-4 +``` + +### Installing Spigot via BuildTools +For versions 1.20.5-1.21.3, we use BuildTools. To install these versions, we run the following commands. +``` +wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O BuildTools.jar + +git clone https://hub.spigotmc.org/stash/scm/spigot/bukkit.git Bukkit +cd Bukkit +git checkout 304e83eb384c338546aa96eea51388e0e8407e26 +cd .. + +git clone https://hub.spigotmc.org/stash/scm/spigot/craftbukkit.git CraftBukkit +cd CraftBukkit +git checkout 91b1fc3f1cf89e2591367dca1fa7362fe376f289 +cd .. + +git clone https://hub.spigotmc.org/stash/scm/spigot/spigot.git Spigot +cd Spigot +git checkout b698b49caf14f97a717afd67e13fd7ac59f51089 +cd .. + +git clone https://hub.spigotmc.org/stash/scm/spigot/builddata.git BuildData +cd BuildData +git checkout a7f7c2118b877fde4cf0f32f1f730ffcdee8e9ee +cd .. + +java -jar BuildTools.jar --remapped --disable-java-check --dont-update +java -jar BuildTools.jar --rev 1.20.6 --remapped --disable-java-check + +cd Bukkit +git checkout 2ec53f498e32b3af989cb24672fc54dfab087154 +cd .. + +cd CraftBukkit +git checkout 8ee6fd1b8db9896590aa321d0199453de1fc35db +cd .. + +cd Spigot +git checkout fb8fb722a327a2f9f097f2ded700ac5de8157408 +cd .. + +cd BuildData +git checkout ae1e7b1e31cd3a3892bb05a6ccdcecc48c73c455 +cd .. + +java -jar BuildTools.jar --remapped --disable-java-check --dont-update +java -jar BuildTools.jar --rev 1.21.1 --remapped --disable-java-check +java -jar BuildTools.jar --rev 1.21.3 --remapped --disable-java-check +``` + +Your environment is now set up correctly. To create a build, run the following inside the root folder of the project. +``` +mvn clean package +``` +Your build is now available in the /IF/target folder. + +## Adventure support + +IF supports [Adventure](https://github.com/KyoriPowered/adventure), but does not shade it in itself. +The use of Adventure `Component`s instead of legacy `String`s is completely optional. +If you do not wish to use Adventure you can safely ignore all `TextHolder` related methods. + +### What is Adventure? + +Adventure is a library that adds proper modern text support to Minecraft. +Modern text is represented using bungee-chat and `BaseComponent` instances in Spigot. +Adventure is an alternative to bungee-chat and offers more features. + +### Using Adventure on 1.16.5+ Paper + +You don't need to import/shade anything for Adventure support in this case! + +*Note: Paper only supports Adventure on build 473 and above. If you aren't running months old builds, then you are fine.* + +### Using Adventure on Spigot and older Paper + +On Spigot Adventure isn't included in the server, therefore you have to shade and relocate it yourself. +The following dependencies need to be imported and shaded: +- adventure-api +- adventure-platform-bukkit + +Please consult the [Adventure documentation](https://docs.adventure.kyori.net/) for more information. + +### How to use Adventure `Component`s + +Example of migration from legacy `String` to Adventure `Component`: + - legacy: `namedGui.setTitle("My Title!");` + - Adventure: `namedGui.setTitle(ComponentHolder.of(Component.text("My Title!")));` + +We apologize for the boilerplate (the `ComponentHolder.of(...)` call), but that was the only way to not make IF hard-depend on Adventure. + +Full Adventure support is only achieved when your server natively supports Adventure (it is running Paper) and your plugin depends on Paper (instead of Spigot). +In other words, you won't benefit from Adventure as much if you use Spigot instead of Paper. +This is because when Adventure is relocated we have to convert everything back to legacy `String`s before passing them to the Bukkit API. diff --git a/adventure-support/pom.xml b/adventure-support/pom.xml new file mode 100644 index 000000000..3d4bb888f --- /dev/null +++ b/adventure-support/pom.xml @@ -0,0 +1,39 @@ + + + + IF-parent + com.github.stefvanschie.inventoryframework + 0.10.18 + + 4.0.0 + + adventure-support + + + true + + + + + papermc + https://papermc.io/repo/repository/maven-public/ + + + + + + com.destroystokyo.paper + paper-api + 1.16.5-R0.1-SNAPSHOT + provided + + + net.kyori + adventure-api + ${adventure.version} + provided + + + \ No newline at end of file diff --git a/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ComponentHolder.java b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ComponentHolder.java new file mode 100644 index 000000000..3d6a4b587 --- /dev/null +++ b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ComponentHolder.java @@ -0,0 +1,176 @@ +package com.github.stefvanschie.inventoryframework.adventuresupport; + +import com.google.gson.JsonElement; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +/** + * Wrapper of an Adventure {@link Component}. + * + * @since 0.10.0 + */ +public abstract class ComponentHolder extends TextHolder { + + /** + * Whether the server platform natively supports Adventure. + * A null value indicates that we don't yet know this: it hasn't been determined yet. + * This field should not be used directly, use {@link #isNativeAdventureSupport()} instead. + */ + @Nullable + private static Boolean nativeAdventureSupport; + + /** + * The serializer to use when converting wrapped values to legacy strings. + * A null value indicates that we haven't created the serializer yet. + * This field should not be used directly, use {@link #getLegacySerializer()} instead. + */ + @Nullable + private static LegacyComponentSerializer legacySerializer; + + /** + * Wraps the specified Adventure component. + * + * @param value the value to wrap + * @return an instance that wraps the specified value + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static ComponentHolder of(@NotNull Component value) { + Validate.notNull(value, "value mustn't be null"); + return isNativeAdventureSupport() + ? new NativeComponentHolder(value) + : new ForeignComponentHolder(value); + } + + /** + * Gets whether the server platform natively supports Adventure. + * Native Adventure support means that eg. {@link ItemMeta#displayName(Component)} + * is a valid method. + * + * @return whether the server platform natively supports Adventure + * @since 0.10.0 + */ + private static boolean isNativeAdventureSupport() { + if (nativeAdventureSupport == null) { + try { + Component component = Component.text("test"); + NativeComponentHolder holder = new NativeComponentHolder(component); + + //If NoSuchMethodError or something is thrown we can assume that + //Adventure components are not natively supported by the server platform + + //noinspection unused + Object ignored1 = holder.asInventoryTitle(null, 9); + //noinspection unused + Object ignored2 = holder.asInventoryTitle(null, InventoryType.HOPPER); + + ItemMeta meta = new ItemStack(Material.STONE).getItemMeta(); + holder.asItemDisplayName(meta); + holder.asItemLoreAtEnd(meta); + + nativeAdventureSupport = true; + } catch (Throwable t) { + nativeAdventureSupport = false; + } + } + return nativeAdventureSupport; + } + + /** + * Gets the serializer to use when converting wrapped values to legacy strings. + * Main use case being the implementation of {@link #asLegacyString()}. + * + * @return a serializer for converting wrapped values to legacy strings + * @since 0.10.0 + */ + private static LegacyComponentSerializer getLegacySerializer() { + if (legacySerializer == null) { + LegacyComponentSerializer.Builder builder = LegacyComponentSerializer.builder() + .character(LegacyComponentSerializer.SECTION_CHAR); + if (!net.md_5.bungee.api.ChatColor.class.isEnum()) { + //1.16+ Spigot (or Paper), hex colors are supported, no need to down sample them + builder.hexColors() + .useUnusualXRepeatedCharacterHexFormat(); + } + legacySerializer = builder.build(); + } + return legacySerializer; + } + + /** + * The Adventure component this instance wraps. + */ + @NotNull + protected final Component value; + + /** + * Creates and initializes a new instance. + * + * @param value the Adventure component this instance should wrap + * @since 0.10.0 + */ + ComponentHolder(@NotNull Component value) { + this.value = value; + } + + /** + * Gets the Adventure component this instance wraps. + * + * @return the contained Adventure component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public Component getComponent() { + return value; + } + + /** + * Gets the wrapped Adventure component in a JSON representation. + * + * @return the contained Adventure component as JSON + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public JsonElement asJson() { + return GsonComponentSerializer.gson().serializeToTree(value); + } + + @NotNull + @Contract(pure = true) + @Override + public String toString() { + return getClass().getSimpleName() + "{" + value + "}"; + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object other) { + return other != null && getClass() == other.getClass() + && Objects.equals(value, ((ComponentHolder) other).value); + } + + @NotNull + @Contract(pure = true) + @Override + public String asLegacyString() { + return getLegacySerializer().serialize(value); + } +} diff --git a/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ForeignComponentHolder.java b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ForeignComponentHolder.java new file mode 100644 index 000000000..ddc7955e9 --- /dev/null +++ b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ForeignComponentHolder.java @@ -0,0 +1,70 @@ +package com.github.stefvanschie.inventoryframework.adventuresupport; + +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.Merchant; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +/** + * A {@link ComponentHolder} implementation for platforms where Adventure isn't natively supported. + * Adventure components are converted to legacy Strings before passed to the Bukkit API. + * + * @see NativeComponentHolder + * @since 0.10.0 + */ +class ForeignComponentHolder extends ComponentHolder { + + /** + * A {@link StringHolder} wrapping {@link #asLegacyString()}. + * This class depends on {@link StringHolder} to reduce code duplication. + */ + @NotNull + private final StringHolder legacy; + + /** + * Creates and initializes a new instance. + * + * @param value the Adventure component this instance should wrap + * @since 0.10.0 + */ + ForeignComponentHolder(@NotNull Component value) { + super(value); + legacy = StringHolder.of(asLegacyString()); + } + + @NotNull + @Contract(pure = true) + @Override + public Inventory asInventoryTitle(InventoryHolder holder, InventoryType type) { + return legacy.asInventoryTitle(holder, type); + } + + @NotNull + @Contract(pure = true) + @Override + public Inventory asInventoryTitle(InventoryHolder holder, int size) { + return legacy.asInventoryTitle(holder, size); + } + + @NotNull + @Contract(pure = true) + @Override + public Merchant asMerchantTitle() { + return legacy.asMerchantTitle(); + } + + @Override + public void asItemDisplayName(ItemMeta meta) { + legacy.asItemDisplayName(meta); + } + + @Override + public void asItemLoreAtEnd(ItemMeta meta) { + legacy.asItemLoreAtEnd(meta); + } +} diff --git a/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/NativeComponentHolder.java b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/NativeComponentHolder.java new file mode 100644 index 000000000..eaea94d11 --- /dev/null +++ b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/NativeComponentHolder.java @@ -0,0 +1,70 @@ +package com.github.stefvanschie.inventoryframework.adventuresupport; + +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.Merchant; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * A {@link ComponentHolder} implementation for platforms where Adventure is natively supported. + * Adventure components are directly passed to the Bukkit (Paper) API. + * + * @see ForeignComponentHolder + * @since 0.10.0 + */ +class NativeComponentHolder extends ComponentHolder { + + /** + * Creates and initializes a new instance. + * + * @param value the Adventure component this instance should wrap + * @since 0.10.0 + */ + NativeComponentHolder(@NotNull Component value) { + super(value); + } + + @NotNull + @Contract(pure = true) + @Override + public Inventory asInventoryTitle(InventoryHolder holder, InventoryType type) { + return Bukkit.createInventory(holder, type, value); + } + + @NotNull + @Contract(pure = true) + @Override + public Inventory asInventoryTitle(InventoryHolder holder, int size) { + return Bukkit.createInventory(holder, size, value); + } + + @NotNull + @Contract(pure = true) + @Override + public Merchant asMerchantTitle() { + return Bukkit.createMerchant(value); + } + + @Override + public void asItemDisplayName(ItemMeta meta) { + meta.displayName(value); + } + + @Override + public void asItemLoreAtEnd(ItemMeta meta) { + List lore = meta.hasLore() + ? Objects.requireNonNull(meta.lore()) + : new ArrayList<>(); + lore.add(value); + meta.lore(lore); + } +} diff --git a/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/StringHolder.java b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/StringHolder.java new file mode 100644 index 000000000..cfbbb7aa6 --- /dev/null +++ b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/StringHolder.java @@ -0,0 +1,138 @@ +package com.github.stefvanschie.inventoryframework.adventuresupport; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.Merchant; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Wrapper of a legacy string value. + * {@link org.bukkit.ChatColor} based formatting is used. + * + * @since 0.10.0 + */ +public final class StringHolder extends TextHolder { + + /** + * Cached instance which wraps an empty {@link String}. + */ + @NotNull + private static final StringHolder EMPTY = StringHolder.of(""); + + /** + * Wraps the specified legacy string. + * + * @param value the value to wrap + * @return an instance that wraps the specified value + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static StringHolder of(@NotNull String value) { + Validate.notNull(value, "value mustn't be null"); + return new StringHolder(value); + } + + /** + * Gets an instance that contains no characters. + * + * @return an instance without any characters + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static StringHolder empty() { + return EMPTY; + } + + /** + * The legacy string this instance wraps. + */ + @NotNull + private final String value; + + /** + * Creates and initializes a new instance. + * + * @param value the legacy string this instance should wrap + * @since 0.10.0 + */ + private StringHolder(@NotNull String value) { + this.value = value; + } + + @NotNull + @Contract(pure = true) + @Override + public String toString() { + return getClass().getSimpleName() + "{" + value + "}"; + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object other) { + return other != null && getClass() == other.getClass() + && Objects.equals(value, ((StringHolder) other).value); + } + + @NotNull + @Contract(pure = true) + @Override + public String asLegacyString() { + return value; + } + + @NotNull + @Contract(pure = true) + @Override + public Inventory asInventoryTitle(InventoryHolder holder, InventoryType type) { + //noinspection deprecation + return Bukkit.createInventory(holder, type, value); + } + + @NotNull + @Contract(pure = true) + @Override + public Inventory asInventoryTitle(InventoryHolder holder, int size) { + //noinspection deprecation + return Bukkit.createInventory(holder, size, value); + } + + @NotNull + @Contract(pure = true) + @Override + public Merchant asMerchantTitle() { + //noinspection deprecation + return Bukkit.createMerchant(value); + } + + @Override + public void asItemDisplayName(ItemMeta meta) { + //noinspection deprecation + meta.setDisplayName(value); + } + + @Override + public void asItemLoreAtEnd(ItemMeta meta) { + //noinspection deprecation + List lore = meta.hasLore() + ? Objects.requireNonNull(meta.getLore()) + : new ArrayList<>(); + lore.add(value); + //noinspection deprecation + meta.setLore(lore); + } +} diff --git a/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/TextHolder.java b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/TextHolder.java new file mode 100644 index 000000000..dea4b8d72 --- /dev/null +++ b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/TextHolder.java @@ -0,0 +1,132 @@ +package com.github.stefvanschie.inventoryframework.adventuresupport; + +import net.kyori.adventure.text.Component; +import org.bukkit.ChatColor; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.Merchant; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +/** + * Immutable wrapper of a text-like value. + * Support for both Adventure and legacy strings is achieved through this class. + * To get an instance of this class please refer to either {@link StringHolder#of(String)} + * or {@link ComponentHolder#of(Component)}. + * Other methods like {@link #empty()} and {@link #deserialize(String)} + * also exist, but their use cases are very limited. + * + * @see StringHolder + * @see ComponentHolder + * @since 0.10.0 + */ +public abstract class TextHolder { + + /** + * Gets an instance that contains no characters and no formatting. + * + * @return an instance without any characters or formatting + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static TextHolder empty() { + return StringHolder.empty(); + } + + /** + * Deserializes the specified {@link String} as a {@link TextHolder}. + * This method is still WIP and may change drastically in the future: + *
    + *
  • Are we going to use MiniMessage if it's present?
  • + *
  • Is MiniMessage going to be opt-in? If yes, how do we opt-in?
  • + *
+ * + * @param string the raw data to deserialize + * @return an instance containing the text from the string + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static TextHolder deserialize(@NotNull String string) { + return StringHolder.of(ChatColor.translateAlternateColorCodes('&', string)); + } + + TextHolder() { + //package-private constructor to "seal" the class + } + + @NotNull + @Contract(pure = true) + @Override + public abstract String toString(); + + @Override + public abstract int hashCode(); + + @Override + public abstract boolean equals(Object other); + + /** + * Converts the text wrapped by this class instance to a legacy string, + * keeping the original formatting. + * + * @return the wrapped value represented as a legacy string + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public abstract String asLegacyString(); + + /** + * Creates a new inventory with the wrapped value as the inventory's title. + * + * @param holder the holder to use for the new inventory + * @param type the type of inventory to create + * @return a newly created inventory with the wrapped value as its title + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public abstract Inventory asInventoryTitle(InventoryHolder holder, InventoryType type); + + /** + * Creates a new inventory with the wrapped value as the inventory's title. + * + * @param holder the holder to use for the new inventory + * @param size the count of slots the inventory should have (normal size restrictions apply) + * @return a newly created inventory with the wrapped value as its title + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public abstract Inventory asInventoryTitle(InventoryHolder holder, int size); + + /** + * Creates a new merchant with the wrapped value as the merchant's title. + * + * @return a newly created inventory with the wrapped value as its title + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public abstract Merchant asMerchantTitle(); + + /** + * Modifies the specified meta: sets the display name to the wrapped value. + * + * @param meta the meta whose display name to set + * @since 0.10.0 + */ + public abstract void asItemDisplayName(ItemMeta meta); + + /** + * Modifies the specified meta: adds the wrapped value as a new lore line at the end + * + * @param meta the meta whose lore to append to + * @since 0.10.0 + */ + public abstract void asItemLoreAtEnd(ItemMeta meta); +} diff --git a/inventory-view/iv-abstract-class/pom.xml b/inventory-view/iv-abstract-class/pom.xml new file mode 100644 index 000000000..ea1a52a81 --- /dev/null +++ b/inventory-view/iv-abstract-class/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + iv-abstract-class + + + true + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + com.github.stefvanschie.inventoryframework + iv-abstraction + ${project.version} + + + org.spigotmc + spigot-api + 1.20.6-R0.1-SNAPSHOT + provided + + + + org.apache.commons + commons-lang3 + + + + + + \ No newline at end of file diff --git a/inventory-view/iv-abstract-class/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstractclass/InventoryViewUtil.java b/inventory-view/iv-abstract-class/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstractclass/InventoryViewUtil.java new file mode 100644 index 000000000..eb8ec2084 --- /dev/null +++ b/inventory-view/iv-abstract-class/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstractclass/InventoryViewUtil.java @@ -0,0 +1,77 @@ +package com.github.stefvanschie.inventoryframework.inventoryview.abstractclass; + +import com.github.stefvanschie.inventoryframework.inventoryview.abstraction.AbstractInventoryViewUtil; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A wrapper for {@link InventoryView} methods that apply when {@link InventoryView} was an abstract class. + * + * @since 0.10.16 + */ +public class InventoryViewUtil implements AbstractInventoryViewUtil { + + /** + * Instance of this singleton class. + */ + @NotNull + private static final InventoryViewUtil INSTANCE = new InventoryViewUtil(); + + @NotNull + @Override + public Inventory getBottomInventory(@NotNull InventoryView view) { + return view.getBottomInventory(); + } + + @Nullable + @Override + public ItemStack getCursor(@NotNull InventoryView view) { + return view.getCursor(); + } + + @Override + public void setCursor(@NotNull InventoryView view, @Nullable ItemStack item) { + view.setCursor(item); + } + + @Nullable + @Override + public Inventory getInventory(@NotNull InventoryView view, int slot) { + return view.getInventory(slot); + } + + @NotNull + @Override + public InventoryType.SlotType getSlotType(@NotNull InventoryView view, int slot) { + return view.getSlotType(slot); + } + + @NotNull + @Override + public String getTitle(@NotNull InventoryView view) { + return view.getTitle(); + } + + @NotNull + @Override + public Inventory getTopInventory(@NotNull InventoryView view) { + return view.getTopInventory(); + } + + /** + * Gets the singleton instance of this class. + * + * @return the instance of this class + * @since 0.10.16 + */ + @NotNull + @Contract(pure = true) + public static InventoryViewUtil getInstance() { + return INSTANCE; + } +} diff --git a/inventory-view/iv-abstraction/pom.xml b/inventory-view/iv-abstraction/pom.xml new file mode 100644 index 000000000..c03fa67b7 --- /dev/null +++ b/inventory-view/iv-abstraction/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + iv-abstraction + + + true + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + org.spigotmc + spigot-api + 1.21-R0.1-SNAPSHOT + provided + + + + org.apache.commons + commons-lang3 + + + + + + \ No newline at end of file diff --git a/inventory-view/iv-abstraction/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstraction/AbstractInventoryViewUtil.java b/inventory-view/iv-abstraction/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstraction/AbstractInventoryViewUtil.java new file mode 100644 index 000000000..8d7f0c6cc --- /dev/null +++ b/inventory-view/iv-abstraction/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstraction/AbstractInventoryViewUtil.java @@ -0,0 +1,87 @@ +package com.github.stefvanschie.inventoryframework.inventoryview.abstraction; + +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A wrapper for {@link InventoryView} methods that apply when {@link InventoryView} was an abstract class. + * + * @since 0.10.16 + */ +public interface AbstractInventoryViewUtil { + + /** + * Behaves according to {@link InventoryView#getBottomInventory()}. + * + * @param view the {@link InventoryView} to invoke {@link InventoryView#getBottomInventory()} on + * @return the result of invoking {@link InventoryView#getBottomInventory()} + * @since 0.10.16 + */ + @NotNull + Inventory getBottomInventory(@NotNull InventoryView view); + + /** + * Behaves according to {@link InventoryView#getCursor()}. + * + * @param view the {@link InventoryView} to invoke {@link InventoryView#getCursor()} on + * @return the result of invoking {@link InventoryView#getCursor()} + * @since 0.10.16 + */ + @Nullable + ItemStack getCursor(@NotNull InventoryView view); + + /** + * Behaves according to {@link InventoryView#setCursor(ItemStack)}. + * + * @param view the {@link InventoryView} to invoke {@link InventoryView#setCursor(ItemStack)} on + * @param item the {@link ItemStack} to apply when invoking {@link InventoryView#setCursor(ItemStack)} + * @since 0.10.16 + */ + void setCursor(@NotNull InventoryView view, @Nullable ItemStack item); + + /** + * Behaves according to {@link InventoryView#getInventory(int)}. + * + * @param view the {@link InventoryView} to invoke {@link InventoryView#getInventory(int)} on + * @param slot the slot to apply when invoking {@link InventoryView#getInventory(int)} + * @return the result of invoking {@link InventoryView#getInventory(int)} + * @since 0.10.16 + */ + @Nullable + Inventory getInventory(@NotNull InventoryView view, int slot); + + /** + * Behaves according to {@link InventoryView#getSlotType(int)}. + * + * @param view the {@link InventoryView} to invoke {@link InventoryView#getSlotType(int)} on + * @param slot the slot to apply when invoking {@link InventoryView#getSlotType(int)} + * @return the result of invoking {@link InventoryView#getSlotType(int)} + * @since 0.10.16 + */ + @NotNull + InventoryType.SlotType getSlotType(@NotNull InventoryView view, int slot); + + /** + * Behaves according to {@link InventoryView#getTitle()}. + * + * @param view the {@link InventoryView} to invoke {@link InventoryView#getTitle()} on + * @return the result of invoking {@link InventoryView#getTitle()} + * @since 0.10.16 + */ + @NotNull + String getTitle(@NotNull InventoryView view); + + /** + * Behaves according to {@link InventoryView#getTopInventory()}. + * + * @param view the {@link InventoryView} to invoke {@link InventoryView#getTopInventory()} on + * @return the result of invoking {@link InventoryView#getTopInventory()} + * @since 0.10.16 + */ + @NotNull + Inventory getTopInventory(@NotNull InventoryView view); +} diff --git a/inventory-view/iv-interface/pom.xml b/inventory-view/iv-interface/pom.xml new file mode 100644 index 000000000..cd6e8cf4c --- /dev/null +++ b/inventory-view/iv-interface/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + iv-interface + + + true + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + com.github.stefvanschie.inventoryframework + iv-abstraction + ${project.version} + + + org.spigotmc + spigot-api + 1.21-R0.1-SNAPSHOT + provided + + + + org.apache.commons + commons-lang3 + + + + + + \ No newline at end of file diff --git a/inventory-view/iv-interface/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/interface_/InventoryViewUtil.java b/inventory-view/iv-interface/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/interface_/InventoryViewUtil.java new file mode 100644 index 000000000..9d94062a0 --- /dev/null +++ b/inventory-view/iv-interface/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/interface_/InventoryViewUtil.java @@ -0,0 +1,77 @@ +package com.github.stefvanschie.inventoryframework.inventoryview.interface_; + +import com.github.stefvanschie.inventoryframework.inventoryview.abstraction.AbstractInventoryViewUtil; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A wrapper for {@link InventoryView} methods that apply when {@link InventoryView} was an abstract class. + * + * @since 0.10.16 + */ +public class InventoryViewUtil implements AbstractInventoryViewUtil { + + /** + * Instance of this singleton class. + */ + @NotNull + private static final InventoryViewUtil INSTANCE = new InventoryViewUtil(); + + @NotNull + @Override + public Inventory getBottomInventory(@NotNull InventoryView view) { + return view.getBottomInventory(); + } + + @Nullable + @Override + public ItemStack getCursor(@NotNull InventoryView view) { + return view.getCursor(); + } + + @Override + public void setCursor(@NotNull InventoryView view, @Nullable ItemStack item) { + view.setCursor(item); + } + + @Nullable + @Override + public Inventory getInventory(@NotNull InventoryView view, int slot) { + return view.getInventory(slot); + } + + @NotNull + @Override + public InventoryType.SlotType getSlotType(@NotNull InventoryView view, int slot) { + return view.getSlotType(slot); + } + + @NotNull + @Override + public String getTitle(@NotNull InventoryView view) { + return view.getTitle(); + } + + @NotNull + @Override + public Inventory getTopInventory(@NotNull InventoryView view) { + return view.getTopInventory(); + } + + /** + * Gets the singleton instance of this class. + * + * @return the instance of this class + * @since 0.10.16 + */ + @NotNull + @Contract(pure = true) + public static InventoryViewUtil getInstance() { + return INSTANCE; + } +} diff --git a/nms/1_14_R1/pom.xml b/nms/1_14/pom.xml similarity index 86% rename from nms/1_14_R1/pom.xml rename to nms/1_14/pom.xml index 6dbcde799..dad32016c 100644 --- a/nms/1_14_R1/pom.xml +++ b/nms/1_14/pom.xml @@ -5,12 +5,12 @@ IF-parent com.github.stefvanschie.inventoryframework - 0.9.8 + 0.10.18 ../../pom.xml 4.0.0 - 1_14_R1 + 1_14 true @@ -18,8 +18,8 @@ - org.spigotmc - spigot + io.papermc + paper 1.14.4-R0.1-SNAPSHOT provided diff --git a/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/AnvilInventoryImpl.java b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/AnvilInventoryImpl.java new file mode 100644 index 000000000..9b30ed8bc --- /dev/null +++ b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/AnvilInventoryImpl.java @@ -0,0 +1,381 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_14; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_14.util.TextHolderUtil; +import net.minecraft.server.v1_14_R1.*; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_14_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Internal anvil inventory for 1.14 R1 + * + * @since 0.8.0 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(entityPlayer); + + entityPlayer.activeContainer = entityPlayer.defaultContainer; + + IChatBaseComponent message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(containerId, Containers.ANVIL, message)); + entityPlayer.activeContainer = containerAnvil; + entityPlayer.syncInventory(); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.a, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.a); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.8.0 + */ + private class ContainerAnvilImpl extends ContainerAnvil { + + /** + * The index of the result slot + */ + private static final int RESULT_SLOT_INDEX = 2; + + /** + * A unique item + */ + @NotNull + private final ItemStack uniqueItem; + + /** + * The field containing the listeners for this container + */ + @NotNull + private final Field listenersField; + + /** + * Creates a new custom anvil container for the specified player + * + * @param entityPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer, @NotNull IChatBaseComponent title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, + ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); + + this.checkReachable = false; + + try { + //stores all the registered container properties + Field dField = Container.class.getDeclaredField("d"); + dField.setAccessible(true); + + //get rid of the level cost property + ((List) dField.get(this)).clear(); + } catch (NoSuchFieldException | IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'd'", exception); + } + + try { + this.listenersField = Container.class.getDeclaredField("listeners"); + this.listenersField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + //register a new property for the level cost + ContainerProperty levelCost = a(new ContainerProperty() { + private int value; + + @Override + public int get() { + return value; + } + + @Override + public void set(int value) { + this.value = value; + } + + /* + This checks whether there have been any changes, but we want to override the client prediction. This + means the server should be sending the data to the client, even if it didn't change server-side. To + force this, we tell the server the data has always changed. + */ + @Override + public boolean c() { + return true; + } + }); + + levelCost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(RESULT_SLOT_INDEX); + + Slot newSlot = new Slot(originalSlot.inventory, originalSlot.index, originalSlot.e, originalSlot.f) { + @Override + public boolean isAllowed(ItemStack itemStack) { + return true; + } + + @Override + public boolean isAllowed(EntityHuman entityHuman) { + return true; + } + + @Override + public ItemStack a(EntityHuman entityHuman, @NotNull ItemStack itemStack) { + return itemStack; + } + }; + + this.slots.set(RESULT_SLOT_INDEX, newSlot); + + this.uniqueItem = new ItemStack(Items.COOKIE); + + //to make the item unique, we add a random uuid as nbt to it + UUID uuid = UUID.randomUUID(); + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + + nbtTagCompound.set("uuid", new NBTTagLongArray(new long [] { + uuid.getLeastSignificantBits(), + uuid.getMostSignificantBits() + })); + + this.uniqueItem.setTag(nbtTagCompound); + } + + @Override + public void a(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + forceUpdate(); + } + + @Override + public void e() {} + + @Override + public void b(EntityHuman entityHuman) {} + + @Override + protected void a(EntityHuman entityHuman, World world, @NotNull IInventory inventory) {} + + @Override + public ItemStack a(int i, int j, InventoryClickType inventoryclicktype, EntityHuman entityhuman) { + ItemStack itemStack = super.a(i, j, inventoryclicktype, entityhuman); + + //the client predicts the allowed movement of the item, so we broadcast the state again to override it + forceUpdate(); + + return itemStack; + } + + public int getContainerId() { + return this.windowId; + } + + /** + * Forcefully updates the client state, sending all items, container properties and the held item. + * + * @since 0.10.8 + */ + public void forceUpdate() { + /* + The server will not send the items when they haven't changed, so we will overwrite every item with a unique + item first to ensure the server is going to send our items. + */ + Collections.fill(this.items, this.uniqueItem); + + c(); + + List listeners; + + try { + //noinspection unchecked + listeners = (List) listenersField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + for (ICrafting listener : listeners) { + if (!(listener instanceof EntityPlayer)) { + continue; + } + + EntityPlayer player = (EntityPlayer) listener; + + player.e = false; + player.broadcastCarriedItem(); + } + } + } +} diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/BeaconInventoryImpl.java b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/BeaconInventoryImpl.java similarity index 95% rename from nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/BeaconInventoryImpl.java rename to nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/BeaconInventoryImpl.java index e6fb366da..407358326 100644 --- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/BeaconInventoryImpl.java +++ b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/BeaconInventoryImpl.java @@ -1,181 +1,181 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_14_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; -import net.minecraft.server.v1_14_R1.*; -import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryBeacon; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal beacon inventory for 1.14 R1 - * - * @since 0.8.0 - */ -public class BeaconInventoryImpl extends BeaconInventory { - - public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item); - - entityPlayer.activeContainer = containerBeacon; - - int id = containerBeacon.windowId; - ChatMessage message = new ChatMessage("Beacon"); - - entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message)); - - sendItem(player, item); - } - - @Override - public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - NonNullList items = NonNullList.a( - ItemStack.a, //the first item doesn't count for some reason, so send a dummy item - CraftItemStack.asNMSCopy(item) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container beacon - * - * @since 0.8.0 - */ - private class ContainerBeaconImpl extends ContainerBeacon { - - /** - * The player for this beacon container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container beacon - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the beacon field - */ - @NotNull - private final Field beaconField; - - public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.beaconField = ContainerBeacon.class.getDeclaredField("beacon"); - this.beaconField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - try { - ItemStack itemStack = CraftItemStack.asNMSCopy(item); - - ((IInventory) beaconField.get(this)).setItem(0, itemStack); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - try { - CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_14; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.server.v1_14_R1.*; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.14 R1 + * + * @since 0.8.0 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item); + + entityPlayer.activeContainer = containerBeacon; + + int id = containerBeacon.windowId; + ChatMessage message = new ChatMessage("Beacon"); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.a( + ItemStack.a, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.8.0 + */ + private class ContainerBeaconImpl extends ContainerBeacon { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.beaconField = ContainerBeacon.class.getDeclaredField("beacon"); + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((IInventory) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + } +} diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/CartographyTableInventoryImpl.java b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/CartographyTableInventoryImpl.java similarity index 94% rename from nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/CartographyTableInventoryImpl.java rename to nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/CartographyTableInventoryImpl.java index 75766664d..fe2798387 100644 --- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/CartographyTableInventoryImpl.java +++ b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/CartographyTableInventoryImpl.java @@ -1,199 +1,201 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_14_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; -import net.minecraft.server.v1_14_R1.*; -import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryCartography; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal cartography table inventory for 1.14 R1 - * - * @since 0.8.0 - */ -public class CartographyTableInventoryImpl extends CartographyTableInventory { - - public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( - entityPlayer, items - ); - - entityPlayer.activeContainer = containerCartographyTable; - - int id = containerCartographyTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.a, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container cartography table - * - * @since 0.8.0 - */ - private class ContainerCartographyTableImpl extends ContainerCartography { - - /** - * The player for this cartography table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container cartography table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - inventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); - - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_14; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_14.util.TextHolderUtil; +import net.minecraft.server.v1_14_R1.*; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.14 R1 + * + * @since 0.8.0 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + entityPlayer, items + ); + + entityPlayer.activeContainer = containerCartographyTable; + + int id = containerCartographyTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.a, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.8.0 + */ + private class ContainerCartographyTableImpl extends ContainerCartography { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultInventoryField; + + public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory"); + this.resultInventoryField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + @NotNull + @Contract(pure = true) + private IInventory getResultInventory() { + try { + return (IInventory) resultInventoryField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/EnchantingTableInventoryImpl.java b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/EnchantingTableInventoryImpl.java similarity index 94% rename from nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/EnchantingTableInventoryImpl.java rename to nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/EnchantingTableInventoryImpl.java index 0acb73900..ae6bb4e44 100644 --- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/EnchantingTableInventoryImpl.java +++ b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/EnchantingTableInventoryImpl.java @@ -1,194 +1,196 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_14_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; -import net.minecraft.server.v1_14_R1.*; -import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryEnchanting; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal enchanting table inventory for 1.14 R1 - * - * @since 0.8.0 - */ -public class EnchantingTableInventoryImpl extends EnchantingTableInventory { - - public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 2) { - throw new IllegalArgumentException( - "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerEnchantmentTable; - - int id = containerEnchantmentTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.a, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container enchanting table - * - * @since 0.8.0 - */ - private class ContainerEnchantingTableImpl extends ContainerEnchantTable { - - /** - * The player for this enchanting table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container enchanting table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the enchant slots field - */ - @NotNull - private final Field enchantSlotsField; - - public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots"); - this.enchantSlotsField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - try { - IInventory input = (IInventory) enchantSlotsField.get(this); - - input.setItem(0, CraftItemStack.asNMSCopy(items[0])); - input.setItem(1, CraftItemStack.asNMSCopy(items[1])); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - try { - CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } catch (IllegalAccessException exception) { - exception.printStackTrace(); - } - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_14; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_14.util.TextHolderUtil; +import net.minecraft.server.v1_14_R1.*; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.14 R1 + * + * @since 0.8.0 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerEnchantmentTable; + + int id = containerEnchantmentTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.a, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.8.0 + */ + private class ContainerEnchantingTableImpl extends ContainerEnchantTable { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots"); + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + IInventory input = (IInventory) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + } +} diff --git a/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/GrindstoneInventoryImpl.java b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..f382990ab --- /dev/null +++ b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/GrindstoneInventoryImpl.java @@ -0,0 +1,272 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_14; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_14.util.TextHolderUtil; +import net.minecraft.server.v1_14_R1.*; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_14_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Internal grindstone inventory for 1.14 R1 + * + * @since 0.8.0 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(entityPlayer); + + entityPlayer.activeContainer = entityPlayer.defaultContainer; + + IChatBaseComponent message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int windowId = containerGrindstone.getWindowId(); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(windowId, Containers.GRINDSTONE, message)); + entityPlayer.activeContainer = containerGrindstone; + entityPlayer.syncInventory(); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = NonNullList.a( + ItemStack.a, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.8.0 + */ + private static class ContainerGrindstoneImpl extends ContainerGrindstone { + + /** + * A unique item + */ + @NotNull + private final ItemStack uniqueItem; + + /** + * The field containing the listeners for this container + */ + @NotNull + private final Field listenersField; + + /** + * Creates a new grindstone container + * + * @param entityPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + try { + this.listenersField = Container.class.getDeclaredField("listeners"); + this.listenersField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.inventory, firstSlot.rawSlotIndex, firstSlot.e, firstSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.inventory, secondSlot.rawSlotIndex, secondSlot.e, secondSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.inventory, thirdSlot.rawSlotIndex, thirdSlot.e, thirdSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + + @Override + public ItemStack a(EntityHuman entityHuman, ItemStack itemStack) { + return itemStack; + } + }); + + this.uniqueItem = new ItemStack(Items.COOKIE); + + //to make the item unique, we add a random uuid as nbt to it + UUID uuid = UUID.randomUUID(); + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + + nbtTagCompound.set("uuid", new NBTTagLongArray(new long [] { + uuid.getLeastSignificantBits(), + uuid.getMostSignificantBits() + })); + + this.uniqueItem.setTag(nbtTagCompound); + } + + /** + * Forcefully updates the client state, sending all items, container properties and the held item. + * + * @since 0.10.8 + */ + public void forceUpdate() { + /* + The server will not send the items when they haven't changed, so we will overwrite every item with a unique + item first to ensure the server is going to send our items. + */ + Collections.fill(this.items, this.uniqueItem); + + c(); + + List listeners; + + try { + //noinspection unchecked + listeners = (List) listenersField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + for (ICrafting listener : listeners) { + if (!(listener instanceof EntityPlayer)) { + continue; + } + + EntityPlayer player = (EntityPlayer) listener; + + player.e = false; + player.broadcastCarriedItem(); + } + } + + @Override + public ItemStack a(int i, int j, InventoryClickType inventoryclicktype, EntityHuman entityhuman) { + ItemStack itemStack = super.a(i, j, inventoryclicktype, entityhuman); + + //the client predicts the allowed movement of the item, so we broadcast the state again to override it + forceUpdate(); + + return itemStack; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityHuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityHuman) {} + + public int getWindowId() { + return this.windowId; + } + } +} diff --git a/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/MerchantInventoryImpl.java b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/MerchantInventoryImpl.java new file mode 100644 index 000000000..326c2ed8d --- /dev/null +++ b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/MerchantInventoryImpl.java @@ -0,0 +1,95 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_14; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.v1_14_R1.EntityPlayer; +import net.minecraft.server.v1_14_R1.MerchantRecipeList; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.14 + * + * @since 0.10.1 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantRecipeList offers = new MerchantRecipeList(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.server.v1_14_R1.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.server.v1_14_R1.ItemStack nmsItemB = net.minecraft.server.v1_14_R1.ItemStack.a; + net.minecraft.server.v1_14_R1.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + net.minecraft.server.v1_14_R1.MerchantRecipe merchantOffer = new net.minecraft.server.v1_14_R1.MerchantRecipe( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPrice(entry.getValue()); + + offers.add(merchantOffer); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + entityPlayer.openTrade(getWindowId(entityPlayer), offers, level, experience, true, false); + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.10.1 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.10.1 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } +} diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/StonecutterInventoryImpl.java b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/StonecutterInventoryImpl.java similarity index 94% rename from nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/StonecutterInventoryImpl.java rename to nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/StonecutterInventoryImpl.java index a193a995d..877624db6 100644 --- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/StonecutterInventoryImpl.java +++ b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/StonecutterInventoryImpl.java @@ -1,199 +1,201 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_14_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; -import net.minecraft.server.v1_14_R1.*; -import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryStonecutter; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal stonecutter inventory for 1.14 R1 - * - * @since 0.8.0 - */ -public class StonecutterInventoryImpl extends StonecutterInventory { - - public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 2) { - throw new IllegalArgumentException( - "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerEnchantmentTable; - - int id = containerEnchantmentTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.a, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container enchanting table - * - * @since 0.8.0 - */ - private class ContainerStonecutterImpl extends ContainerStonecutter { - - /** - * The player for this enchanting table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container enchanting table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - /** - * Gets the result inventory - * - * @return the result inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - public IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_14; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_14.util.TextHolderUtil; +import net.minecraft.server.v1_14_R1.*; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.14 R1 + * + * @since 0.8.0 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerEnchantmentTable; + + int id = containerEnchantmentTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.a, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.8.0 + */ + private class ContainerStonecutterImpl extends ContainerStonecutter { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultInventoryField; + + public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory"); + this.resultInventoryField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + public IInventory getResultInventory() { + try { + return (IInventory) resultInventoryField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/util/TextHolderUtil.java b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/util/TextHolderUtil.java new file mode 100644 index 000000000..5f628de0d --- /dev/null +++ b/nms/1_14/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14/util/TextHolderUtil.java @@ -0,0 +1,66 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_14.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.server.v1_14_R1.ChatComponentText; +import net.minecraft.server.v1_14_R1.IChatBaseComponent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.0 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static IChatBaseComponent toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static IChatBaseComponent toComponent(@NotNull StringHolder holder) { + return new ChatComponentText(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static IChatBaseComponent toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(IChatBaseComponent.ChatSerializer.a(holder.asJson())); + } +} diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/AnvilInventoryImpl.java b/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/AnvilInventoryImpl.java deleted file mode 100644 index a17945e39..000000000 --- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/AnvilInventoryImpl.java +++ /dev/null @@ -1,304 +0,0 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_14_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; -import net.minecraft.server.v1_14_R1.*; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryAnvil; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal anvil inventory for 1.14 R1 - * - * @since 0.8.0 - */ -public class AnvilInventoryImpl extends AnvilInventory { - - public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerAnvil; - - int id = containerAnvil.windowId; - ChatMessage message = new ChatMessage(title); - - entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.ANVIL, message)); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.a, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); - } - - @Override - public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); - } - - @Override - public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - sendResultItem(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearResultItem(@NotNull Player player) { - sendResultItem(player, ItemStack.a); - } - - @Override - public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { - setCursor(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Sets the cursor of the given player - * - * @param player the player to set the cursor - * @param item the item to set the cursor to - * @since 0.8.0 - */ - private void setCursor(@NotNull Player player, @NotNull ItemStack item) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); - } - - /** - * Sends the result item to the specified player with the given item - * - * @param player the player to send the result item to - * @param item the result item - * @since 0.8.0 - */ - private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container anvil for responding to item renaming - * - * @since 0.8.0 - */ - private class ContainerAnvilImpl extends ContainerAnvil { - - /** - * The player for whom this anvil container is - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container anvil - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the repair inventory field - */ - @NotNull - private final Field repairInventoryField; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - /** - * Field for accessing the container access field - */ - @NotNull - private final Field containerAccessField; - - /** - * Creates a new custom anvil container for the specified player - * - * @param entityPlayer the player for who this anvil container is - * @since 0.8.0 - */ - public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, - ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); - - this.player = entityPlayer.getBukkitEntity(); - - try { - repairInventoryField = ContainerAnvil.class.getDeclaredField("repairInventory"); - repairInventoryField.setAccessible(true); - - resultInventoryField = ContainerAnvil.class.getDeclaredField("resultInventory"); - resultInventoryField.setAccessible(true); - - containerAccessField = ContainerAnvil.class.getDeclaredField("containerAccess"); - containerAccessField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - getRepairInventory().setItem(0, CraftItemStack.asNMSCopy(items[0])); - getRepairInventory().setItem(1, CraftItemStack.asNMSCopy(items[1])); - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - Location location = getContainerAccess().getLocation(); - CraftInventory inventory = new CraftInventoryAnvil(location, getRepairInventory(), getResultInventory(), - this) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Override - public void a(@Nullable String name) { - text = name == null ? "" : name; - - sendResultItem(player, getResultInventory().getItem(0)); - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - @NotNull - @Contract(pure = true) - private IInventory getRepairInventory() { - try { - return (IInventory) repairInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - @NotNull - @Contract(pure = true) - private ContainerAccess getContainerAccess() { - try { - return (ContainerAccess) containerAccessField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/GrindstoneInventoryImpl.java b/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/GrindstoneInventoryImpl.java deleted file mode 100644 index c93769a78..000000000 --- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/GrindstoneInventoryImpl.java +++ /dev/null @@ -1,227 +0,0 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_14_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; -import net.minecraft.server.v1_14_R1.*; -import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryGrindstone; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal grindstone inventory for 1.14 R1 - * - * @since 0.8.0 - */ -public class GrindstoneInventoryImpl extends GrindstoneInventory { - - public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerGrindstone; - - int id = containerGrindstone.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.GRINDSTONE, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.a, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container grindstone - * - * @since 0.8.0 - */ - private class ContainerGrindstoneImpl extends ContainerGrindstone { - - /** - * The player for this grindstone container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container grindstone - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the craft inventory field - */ - @NotNull - private final Field craftInventoryField; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.craftInventoryField = ContainerGrindstone.class.getDeclaredField("craftInventory"); - this.craftInventoryField.setAccessible(true); - - this.resultInventoryField = ContainerGrindstone.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - getCraftInventory().setItem(0, CraftItemStack.asNMSCopy(items[0])); - getCraftInventory().setItem(1, CraftItemStack.asNMSCopy(items[1])); - - getResultInventory().setItem(2, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryGrindstone(getCraftInventory(), getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - /** - * Gets the craft inventory - * - * @return the craft inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private IInventory getCraftInventory() { - try { - return (IInventory) craftInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - /** - * Gets the result inventory - * - * @return the result inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} diff --git a/nms/1_15_R1/pom.xml b/nms/1_15/pom.xml similarity index 86% rename from nms/1_15_R1/pom.xml rename to nms/1_15/pom.xml index 8cbdc5417..c85530eb7 100644 --- a/nms/1_15_R1/pom.xml +++ b/nms/1_15/pom.xml @@ -5,12 +5,12 @@ IF-parent com.github.stefvanschie.inventoryframework - 0.9.8 + 0.10.18 ../../pom.xml 4.0.0 - 1_15_R1 + 1_15 true @@ -18,8 +18,8 @@ - org.spigotmc - spigot + io.papermc + paper 1.15.2-R0.1-SNAPSHOT provided diff --git a/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/AnvilInventoryImpl.java b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/AnvilInventoryImpl.java new file mode 100644 index 000000000..aff83a514 --- /dev/null +++ b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/AnvilInventoryImpl.java @@ -0,0 +1,381 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_15; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_15.util.TextHolderUtil; +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_15_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Internal anvil inventory for 1.15 R1 + * + * @since 0.8.0 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(entityPlayer); + + entityPlayer.activeContainer = entityPlayer.defaultContainer; + + IChatBaseComponent message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(containerId, Containers.ANVIL, message)); + entityPlayer.activeContainer = containerAnvil; + entityPlayer.syncInventory(); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.a, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.a); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.8.0 + */ + private class ContainerAnvilImpl extends ContainerAnvil { + + /** + * The index of the result slot + */ + private static final int RESULT_SLOT_INDEX = 2; + + /** + * A unique item + */ + @NotNull + private final ItemStack uniqueItem; + + /** + * The field containing the listeners for this container + */ + @NotNull + private final Field listenersField; + + /** + * Creates a new custom anvil container for the specified player + * + * @param entityPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer, @NotNull IChatBaseComponent title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, + ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); + + this.checkReachable = false; + + try { + //stores all the registered container properties + Field dField = Container.class.getDeclaredField("d"); + dField.setAccessible(true); + + //get rid of the level cost property + ((List) dField.get(this)).clear(); + } catch (NoSuchFieldException | IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'd'", exception); + } + + try { + this.listenersField = Container.class.getDeclaredField("listeners"); + this.listenersField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + //register a new property for the level cost + ContainerProperty levelCost = a(new ContainerProperty() { + private int value; + + @Override + public int get() { + return value; + } + + @Override + public void set(int value) { + this.value = value; + } + + /* + This checks whether there have been any changes, but we want to override the client prediction. This + means the server should be sending the data to the client, even if it didn't change server-side. To + force this, we tell the server the data has always changed. + */ + @Override + public boolean c() { + return true; + } + }); + + levelCost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(RESULT_SLOT_INDEX); + + Slot newSlot = new Slot(originalSlot.inventory, originalSlot.index, originalSlot.e, originalSlot.f) { + @Override + public boolean isAllowed(ItemStack itemStack) { + return true; + } + + @Override + public boolean isAllowed(EntityHuman entityHuman) { + return true; + } + + @Override + public ItemStack a(EntityHuman entityHuman, @NotNull ItemStack itemStack) { + return itemStack; + } + }; + + this.slots.set(RESULT_SLOT_INDEX, newSlot); + + this.uniqueItem = new ItemStack(Items.COOKIE); + + //to make the item unique, we add a random uuid as nbt to it + UUID uuid = UUID.randomUUID(); + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + + nbtTagCompound.set("uuid", new NBTTagLongArray(new long [] { + uuid.getLeastSignificantBits(), + uuid.getMostSignificantBits() + })); + + this.uniqueItem.setTag(nbtTagCompound); + } + + @Override + public void a(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + forceUpdate(); + } + + @Override + public void e() {} + + @Override + public void b(EntityHuman entityHuman) {} + + @Override + protected void a(EntityHuman entityHuman, World world, @NotNull IInventory inventory) {} + + @Override + public ItemStack a(int i, int j, InventoryClickType inventoryclicktype, EntityHuman entityhuman) { + ItemStack itemStack = super.a(i, j, inventoryclicktype, entityhuman); + + //the client predicts the allowed movement of the item, so we broadcast the state again to override it + forceUpdate(); + + return itemStack; + } + + public int getContainerId() { + return this.windowId; + } + + /** + * Forcefully updates the client state, sending all items, container properties and the held item. + * + * @since 0.10.8 + */ + public void forceUpdate() { + /* + The server will not send the items when they haven't changed, so we will overwrite every item with a unique + item first to ensure the server is going to send our items. + */ + Collections.fill(this.items, this.uniqueItem); + + c(); + + List listeners; + + try { + //noinspection unchecked + listeners = (List) listenersField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + for (ICrafting listener : listeners) { + if (!(listener instanceof EntityPlayer)) { + continue; + } + + EntityPlayer player = (EntityPlayer) listener; + + player.e = false; + player.broadcastCarriedItem(); + } + } + } +} diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/BeaconInventoryImpl.java b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/BeaconInventoryImpl.java similarity index 95% rename from nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/BeaconInventoryImpl.java rename to nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/BeaconInventoryImpl.java index 38d7d21df..f6cc4814a 100644 --- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/BeaconInventoryImpl.java +++ b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/BeaconInventoryImpl.java @@ -1,181 +1,181 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_15_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; -import net.minecraft.server.v1_15_R1.*; -import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryBeacon; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal beacon inventory for 1.15 R1 - * - * @since 0.8.0 - */ -public class BeaconInventoryImpl extends BeaconInventory { - - public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item); - - entityPlayer.activeContainer = containerBeacon; - - int id = containerBeacon.windowId; - ChatMessage message = new ChatMessage("Beacon"); - - entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message)); - - sendItem(player, item); - } - - @Override - public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - NonNullList items = NonNullList.a( - ItemStack.a, //the first item doesn't count for some reason, so send a dummy item - CraftItemStack.asNMSCopy(item) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container beacon - * - * @since 0.8.0 - */ - private class ContainerBeaconImpl extends ContainerBeacon { - - /** - * The player for this beacon container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container beacon - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the beacon field - */ - @NotNull - private final Field beaconField; - - public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.beaconField = ContainerBeacon.class.getDeclaredField("beacon"); - this.beaconField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - try { - ItemStack itemStack = CraftItemStack.asNMSCopy(item); - - ((IInventory) beaconField.get(this)).setItem(0, itemStack); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - try { - CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_15; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.15 R1 + * + * @since 0.8.0 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item); + + entityPlayer.activeContainer = containerBeacon; + + int id = containerBeacon.windowId; + ChatMessage message = new ChatMessage("Beacon"); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.a( + ItemStack.a, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.8.0 + */ + private class ContainerBeaconImpl extends ContainerBeacon { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.beaconField = ContainerBeacon.class.getDeclaredField("beacon"); + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((IInventory) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + } +} diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/CartographyTableInventoryImpl.java b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/CartographyTableInventoryImpl.java similarity index 94% rename from nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/CartographyTableInventoryImpl.java rename to nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/CartographyTableInventoryImpl.java index 9748a472a..bc6ada39f 100644 --- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/CartographyTableInventoryImpl.java +++ b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/CartographyTableInventoryImpl.java @@ -1,199 +1,201 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_15_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; -import net.minecraft.server.v1_15_R1.*; -import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryCartography; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal cartography table inventory for 1.15 R1 - * - * @since 0.8.0 - */ -public class CartographyTableInventoryImpl extends CartographyTableInventory { - - public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( - entityPlayer, items - ); - - entityPlayer.activeContainer = containerCartographyTable; - - int id = containerCartographyTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.a, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container cartography table - * - * @since 0.8.0 - */ - private class ContainerCartographyTableImpl extends ContainerCartography { - - /** - * The player for this cartography table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container cartography table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - inventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); - - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_15; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_15.util.TextHolderUtil; +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.15 R1 + * + * @since 0.8.0 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + entityPlayer, items + ); + + entityPlayer.activeContainer = containerCartographyTable; + + int id = containerCartographyTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.a, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.8.0 + */ + private class ContainerCartographyTableImpl extends ContainerCartography { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultInventoryField; + + public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory"); + this.resultInventoryField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + @NotNull + @Contract(pure = true) + private IInventory getResultInventory() { + try { + return (IInventory) resultInventoryField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/EnchantingTableInventoryImpl.java b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/EnchantingTableInventoryImpl.java similarity index 94% rename from nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/EnchantingTableInventoryImpl.java rename to nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/EnchantingTableInventoryImpl.java index 42018e6c9..280f3e242 100644 --- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/EnchantingTableInventoryImpl.java +++ b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/EnchantingTableInventoryImpl.java @@ -1,194 +1,196 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_15_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; -import net.minecraft.server.v1_15_R1.*; -import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryEnchanting; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal enchanting table inventory for 1.15 R1 - * - * @since 0.8.0 - */ -public class EnchantingTableInventoryImpl extends EnchantingTableInventory { - - public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 2) { - throw new IllegalArgumentException( - "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerEnchantmentTable; - - int id = containerEnchantmentTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.a, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container enchanting table - * - * @since 0.8.0 - */ - private class ContainerEnchantingTableImpl extends ContainerEnchantTable { - - /** - * The player for this enchanting table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container enchanting table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the enchant slots field - */ - @NotNull - private final Field enchantSlotsField; - - public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots"); - this.enchantSlotsField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - try { - IInventory input = (IInventory) enchantSlotsField.get(this); - - input.setItem(0, CraftItemStack.asNMSCopy(items[0])); - input.setItem(1, CraftItemStack.asNMSCopy(items[1])); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - try { - CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } catch (IllegalAccessException exception) { - exception.printStackTrace(); - } - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_15; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_15.util.TextHolderUtil; +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.15 R1 + * + * @since 0.8.0 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerEnchantmentTable; + + int id = containerEnchantmentTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.a, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.8.0 + */ + private class ContainerEnchantingTableImpl extends ContainerEnchantTable { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots"); + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + IInventory input = (IInventory) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + } +} diff --git a/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/GrindstoneInventoryImpl.java b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..e34d4fbdd --- /dev/null +++ b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/GrindstoneInventoryImpl.java @@ -0,0 +1,272 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_15; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_15.util.TextHolderUtil; +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_15_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Internal grindstone inventory for 1.15 R1 + * + * @since 0.8.0 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(entityPlayer); + + entityPlayer.activeContainer = entityPlayer.defaultContainer; + + IChatBaseComponent message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int windowId = containerGrindstone.getWindowId(); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(windowId, Containers.GRINDSTONE, message)); + entityPlayer.activeContainer = containerGrindstone; + entityPlayer.syncInventory(); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = NonNullList.a( + ItemStack.a, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.8.0 + */ + private static class ContainerGrindstoneImpl extends ContainerGrindstone { + + /** + * A unique item + */ + @NotNull + private final ItemStack uniqueItem; + + /** + * The field containing the listeners for this container + */ + @NotNull + private final Field listenersField; + + /** + * Creates a new grindstone container + * + * @param entityPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + try { + this.listenersField = Container.class.getDeclaredField("listeners"); + this.listenersField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.inventory, firstSlot.rawSlotIndex, firstSlot.e, firstSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.inventory, secondSlot.rawSlotIndex, secondSlot.e, secondSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.inventory, thirdSlot.rawSlotIndex, thirdSlot.e, thirdSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + + @Override + public ItemStack a(EntityHuman entityHuman, ItemStack itemStack) { + return itemStack; + } + }); + + this.uniqueItem = new ItemStack(Items.COOKIE); + + //to make the item unique, we add a random uuid as nbt to it + UUID uuid = UUID.randomUUID(); + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + + nbtTagCompound.set("uuid", new NBTTagLongArray(new long [] { + uuid.getLeastSignificantBits(), + uuid.getMostSignificantBits() + })); + + this.uniqueItem.setTag(nbtTagCompound); + } + + /** + * Forcefully updates the client state, sending all items, container properties and the held item. + * + * @since 0.10.8 + */ + public void forceUpdate() { + /* + The server will not send the items when they haven't changed, so we will overwrite every item with a unique + item first to ensure the server is going to send our items. + */ + Collections.fill(this.items, this.uniqueItem); + + c(); + + List listeners; + + try { + //noinspection unchecked + listeners = (List) listenersField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + for (ICrafting listener : listeners) { + if (!(listener instanceof EntityPlayer)) { + continue; + } + + EntityPlayer player = (EntityPlayer) listener; + + player.e = false; + player.broadcastCarriedItem(); + } + } + + @Override + public ItemStack a(int i, int j, InventoryClickType inventoryclicktype, EntityHuman entityhuman) { + ItemStack itemStack = super.a(i, j, inventoryclicktype, entityhuman); + + //the client predicts the allowed movement of the item, so we broadcast the state again to override it + forceUpdate(); + + return itemStack; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityHuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityHuman) {} + + public int getWindowId() { + return this.windowId; + } + } +} diff --git a/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/MerchantInventoryImpl.java b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/MerchantInventoryImpl.java new file mode 100644 index 000000000..2b296f202 --- /dev/null +++ b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/MerchantInventoryImpl.java @@ -0,0 +1,95 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_15; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.v1_15_R1.EntityPlayer; +import net.minecraft.server.v1_15_R1.MerchantRecipeList; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.15 + * + * @since 0.10.1 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantRecipeList offers = new MerchantRecipeList(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.server.v1_15_R1.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.server.v1_15_R1.ItemStack nmsItemB = net.minecraft.server.v1_15_R1.ItemStack.a; + net.minecraft.server.v1_15_R1.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + net.minecraft.server.v1_15_R1.MerchantRecipe merchantOffer = new net.minecraft.server.v1_15_R1.MerchantRecipe( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPrice(entry.getValue()); + + offers.add(merchantOffer); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + entityPlayer.openTrade(getWindowId(entityPlayer), offers, level, experience, true, false); + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.10.1 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.10.1 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } +} diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/StonecutterInventoryImpl.java b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/StonecutterInventoryImpl.java similarity index 94% rename from nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/StonecutterInventoryImpl.java rename to nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/StonecutterInventoryImpl.java index ded6bb0d1..88118970e 100644 --- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/StonecutterInventoryImpl.java +++ b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/StonecutterInventoryImpl.java @@ -1,199 +1,201 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_15_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; -import net.minecraft.server.v1_15_R1.*; -import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryStonecutter; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal stonecutter inventory for 1.15 R1 - * - * @since 0.8.0 - */ -public class StonecutterInventoryImpl extends StonecutterInventory { - - public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 2) { - throw new IllegalArgumentException( - "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerEnchantmentTable; - - int id = containerEnchantmentTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.a, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container enchanting table - * - * @since 0.8.0 - */ - private class ContainerStonecutterImpl extends ContainerStonecutter { - - /** - * The player for this enchanting table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container enchanting table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - /** - * Gets the result inventory - * - * @return the result inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - public IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_15; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_15.util.TextHolderUtil; +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.15 R1 + * + * @since 0.8.0 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerEnchantmentTable; + + int id = containerEnchantmentTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.a, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.8.0 + */ + private class ContainerStonecutterImpl extends ContainerStonecutter { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultInventoryField; + + public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory"); + this.resultInventoryField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + public IInventory getResultInventory() { + try { + return (IInventory) resultInventoryField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/util/TextHolderUtil.java b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/util/TextHolderUtil.java new file mode 100644 index 000000000..7554a5bc6 --- /dev/null +++ b/nms/1_15/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15/util/TextHolderUtil.java @@ -0,0 +1,66 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_15.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.server.v1_15_R1.ChatComponentText; +import net.minecraft.server.v1_15_R1.IChatBaseComponent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.0 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static IChatBaseComponent toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static IChatBaseComponent toComponent(@NotNull StringHolder holder) { + return new ChatComponentText(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static IChatBaseComponent toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(IChatBaseComponent.ChatSerializer.a(holder.asJson())); + } +} diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/AnvilInventoryImpl.java b/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/AnvilInventoryImpl.java deleted file mode 100644 index 2cbfc8c58..000000000 --- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/AnvilInventoryImpl.java +++ /dev/null @@ -1,304 +0,0 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_15_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; -import net.minecraft.server.v1_15_R1.*; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryAnvil; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal anvil inventory for 1.15 R1 - * - * @since 0.8.0 - */ -public class AnvilInventoryImpl extends AnvilInventory { - - public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerAnvil; - - int id = containerAnvil.windowId; - ChatMessage message = new ChatMessage(title); - - entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.ANVIL, message)); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.a, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); - } - - @Override - public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); - } - - @Override - public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - sendResultItem(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearResultItem(@NotNull Player player) { - sendResultItem(player, ItemStack.a); - } - - @Override - public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { - setCursor(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Sets the cursor of the given player - * - * @param player the player to set the cursor - * @param item the item to set the cursor to - * @since 0.8.0 - */ - private void setCursor(@NotNull Player player, @NotNull ItemStack item) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); - } - - /** - * Sends the result item to the specified player with the given item - * - * @param player the player to send the result item to - * @param item the result item - * @since 0.8.0 - */ - private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container anvil for responding to item renaming - * - * @since 0.8.0 - */ - private class ContainerAnvilImpl extends ContainerAnvil { - - /** - * The player for whom this anvil container is - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container anvil - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the repair inventory field - */ - @NotNull - private final Field repairInventoryField; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - /** - * Field for accessing the container access field - */ - @NotNull - private final Field containerAccessField; - - /** - * Creates a new custom anvil container for the specified player - * - * @param entityPlayer the player for who this anvil container is - * @since 0.8.0 - */ - public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, - ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); - - this.player = entityPlayer.getBukkitEntity(); - - try { - repairInventoryField = ContainerAnvil.class.getDeclaredField("repairInventory"); - repairInventoryField.setAccessible(true); - - resultInventoryField = ContainerAnvil.class.getDeclaredField("resultInventory"); - resultInventoryField.setAccessible(true); - - containerAccessField = ContainerAnvil.class.getDeclaredField("containerAccess"); - containerAccessField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - getRepairInventory().setItem(0, CraftItemStack.asNMSCopy(items[0])); - getRepairInventory().setItem(1, CraftItemStack.asNMSCopy(items[1])); - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - Location location = getContainerAccess().getLocation(); - CraftInventory inventory = new CraftInventoryAnvil(location, getRepairInventory(), getResultInventory(), - this) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Override - public void a(@Nullable String name) { - text = name == null ? "" : name; - - sendResultItem(player, getResultInventory().getItem(0)); - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - @NotNull - @Contract(pure = true) - private IInventory getRepairInventory() { - try { - return (IInventory) repairInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - @NotNull - @Contract(pure = true) - private ContainerAccess getContainerAccess() { - try { - return (ContainerAccess) containerAccessField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/GrindstoneInventoryImpl.java b/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/GrindstoneInventoryImpl.java deleted file mode 100644 index d582f5fcc..000000000 --- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/GrindstoneInventoryImpl.java +++ /dev/null @@ -1,227 +0,0 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_15_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; -import net.minecraft.server.v1_15_R1.*; -import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryGrindstone; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal grindstone inventory for 1.15 R1 - * - * @since 0.8.0 - */ -public class GrindstoneInventoryImpl extends GrindstoneInventory { - - public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerGrindstone; - - int id = containerGrindstone.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.GRINDSTONE, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.a, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container grindstone - * - * @since 0.8.0 - */ - private class ContainerGrindstoneImpl extends ContainerGrindstone { - - /** - * The player for this grindstone container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container grindstone - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the craft inventory field - */ - @NotNull - private final Field craftInventoryField; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.craftInventoryField = ContainerGrindstone.class.getDeclaredField("craftInventory"); - this.craftInventoryField.setAccessible(true); - - this.resultInventoryField = ContainerGrindstone.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - getCraftInventory().setItem(0, CraftItemStack.asNMSCopy(items[0])); - getCraftInventory().setItem(1, CraftItemStack.asNMSCopy(items[1])); - - getResultInventory().setItem(2, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryGrindstone(getCraftInventory(), getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - /** - * Gets the craft inventory - * - * @return the craft inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private IInventory getCraftInventory() { - try { - return (IInventory) craftInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - /** - * Gets the result inventory - * - * @return the result inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} diff --git a/nms/1_16_R1/pom.xml b/nms/1_16_1/pom.xml similarity index 86% rename from nms/1_16_R1/pom.xml rename to nms/1_16_1/pom.xml index 47e02eab6..6ad0fba2a 100644 --- a/nms/1_16_R1/pom.xml +++ b/nms/1_16_1/pom.xml @@ -5,12 +5,12 @@ IF-parent com.github.stefvanschie.inventoryframework - 0.9.8 + 0.10.18 ../../pom.xml 4.0.0 - 1_16_R1 + 1_16_1 true @@ -18,8 +18,8 @@ - org.spigotmc - spigot + io.papermc + paper 1.16.1-R0.1-SNAPSHOT provided diff --git a/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/AnvilInventoryImpl.java b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/AnvilInventoryImpl.java new file mode 100644 index 000000000..098c90241 --- /dev/null +++ b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/AnvilInventoryImpl.java @@ -0,0 +1,386 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_1; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_1.util.TextHolderUtil; +import net.minecraft.server.v1_16_R1.*; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Internal anvil inventory for 1.16 R1 + * + * @since 0.8.0 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(entityPlayer); + + entityPlayer.activeContainer = entityPlayer.defaultContainer; + + IChatBaseComponent message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(containerId, Containers.ANVIL, message)); + entityPlayer.activeContainer = containerAnvil; + entityPlayer.syncInventory(); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.b); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.8.0 + */ + private class ContainerAnvilImpl extends ContainerAnvil { + + /** + * The index of the result slot + */ + private static final int RESULT_SLOT_INDEX = 2; + + /** + * A unique item + */ + @NotNull + private final ItemStack uniqueItem; + + /** + * The field containing the listeners for this container + */ + @NotNull + private final Field listenersField; + + /** + * Creates a new custom anvil container for the specified player + * + * @param entityPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer, @NotNull IChatBaseComponent title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, + ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); + + this.checkReachable = false; + + try { + //stores all the registered container properties + Field dField = Container.class.getDeclaredField("d"); + dField.setAccessible(true); + + //get rid of the level cost property + ((List) dField.get(this)).clear(); + } catch (NoSuchFieldException | IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'd'", exception); + } + + try { + this.listenersField = Container.class.getDeclaredField("listeners"); + this.listenersField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + //register a new property for the level cost + ContainerProperty levelCost = a(new ContainerProperty() { + private int value; + + @Override + public int get() { + return value; + } + + @Override + public void set(int value) { + this.value = value; + } + + /* + This checks whether there have been any changes, but we want to override the client prediction. This + means the server should be sending the data to the client, even if it didn't change server-side. To + force this, we tell the server the data has always changed. + */ + @Override + public boolean c() { + return true; + } + }); + + levelCost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(RESULT_SLOT_INDEX); + + Slot newSlot = new Slot(originalSlot.inventory, originalSlot.index, originalSlot.e, originalSlot.f) { + @Override + public boolean isAllowed(ItemStack itemStack) { + return true; + } + + @Override + public boolean isAllowed(EntityHuman entityHuman) { + return true; + } + + @Override + public ItemStack a(EntityHuman entityHuman, @NotNull ItemStack itemStack) { + return originalSlot.a(entityHuman, itemStack); + } + }; + + this.slots.set(RESULT_SLOT_INDEX, newSlot); + + this.uniqueItem = new ItemStack(Items.COOKIE); + + //to make the item unique, we add a random uuid as nbt to it + UUID uuid = UUID.randomUUID(); + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + + nbtTagCompound.set("uuid", new NBTTagLongArray(new long [] { + uuid.getLeastSignificantBits(), + uuid.getMostSignificantBits() + })); + + this.uniqueItem.setTag(nbtTagCompound); + } + + @Override + public void a(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + forceUpdate(); + } + + @Override + public void e() {} + + @Override + public void b(EntityHuman entityHuman) {} + + @Override + protected void a(EntityHuman entityHuman, World world, @NotNull IInventory inventory) {} + + @Override + protected ItemStack a(EntityHuman entityHuman, @NotNull ItemStack itemStack) { + return itemStack; + } + + @Override + public ItemStack a(int i, int j, InventoryClickType inventoryclicktype, EntityHuman entityhuman) { + ItemStack itemStack = super.a(i, j, inventoryclicktype, entityhuman); + + //the client predicts the allowed movement of the item, so we broadcast the state again to override it + forceUpdate(); + + return itemStack; + } + + public int getContainerId() { + return this.windowId; + } + + /** + * Forcefully updates the client state, sending all items, container properties and the held item. + * + * @since 0.10.8 + */ + public void forceUpdate() { + /* + The server will not send the items when they haven't changed, so we will overwrite every item with a unique + item first to ensure the server is going to send our items. + */ + Collections.fill(this.items, this.uniqueItem); + + c(); + + List listeners; + + try { + //noinspection unchecked + listeners = (List) listenersField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + for (ICrafting listener : listeners) { + if (!(listener instanceof EntityPlayer)) { + continue; + } + + EntityPlayer player = (EntityPlayer) listener; + + player.e = false; + player.broadcastCarriedItem(); + } + } + } +} diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/BeaconInventoryImpl.java b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/BeaconInventoryImpl.java similarity index 95% rename from nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/BeaconInventoryImpl.java rename to nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/BeaconInventoryImpl.java index 3bfc1f68b..68183b72b 100644 --- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/BeaconInventoryImpl.java +++ b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/BeaconInventoryImpl.java @@ -1,181 +1,181 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; -import net.minecraft.server.v1_16_R1.*; -import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryBeacon; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal beacon inventory for 1.16 R1 - * - * @since 0.8.0 - */ -public class BeaconInventoryImpl extends BeaconInventory { - - public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item); - - entityPlayer.activeContainer = containerBeacon; - - int id = containerBeacon.windowId; - ChatMessage message = new ChatMessage("Beacon"); - - entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message)); - - sendItem(player, item); - } - - @Override - public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - NonNullList items = NonNullList.a( - ItemStack.b, //the first item doesn't count for some reason, so send a dummy item - CraftItemStack.asNMSCopy(item) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container beacon - * - * @since 0.8.0 - */ - private class ContainerBeaconImpl extends ContainerBeacon { - - /** - * The player for this beacon container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container beacon - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the beacon field - */ - @NotNull - private final Field beaconField; - - public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.beaconField = ContainerBeacon.class.getDeclaredField("beacon"); - this.beaconField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - try { - ItemStack itemStack = CraftItemStack.asNMSCopy(item); - - ((IInventory) beaconField.get(this)).setItem(0, itemStack); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - try { - CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_1; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.server.v1_16_R1.*; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.16 R1 + * + * @since 0.8.0 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item); + + entityPlayer.activeContainer = containerBeacon; + + int id = containerBeacon.windowId; + ChatMessage message = new ChatMessage("Beacon"); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.a( + ItemStack.b, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.8.0 + */ + private class ContainerBeaconImpl extends ContainerBeacon { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.beaconField = ContainerBeacon.class.getDeclaredField("beacon"); + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((IInventory) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + } +} diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/CartographyTableInventoryImpl.java b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/CartographyTableInventoryImpl.java similarity index 94% rename from nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/CartographyTableInventoryImpl.java rename to nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/CartographyTableInventoryImpl.java index e4d3895a0..b80f7bafb 100644 --- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/CartographyTableInventoryImpl.java +++ b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/CartographyTableInventoryImpl.java @@ -1,199 +1,201 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; -import net.minecraft.server.v1_16_R1.*; -import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryCartography; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal cartography table inventory for 1.16 R1 - * - * @since 0.8.0 - */ -public class CartographyTableInventoryImpl extends CartographyTableInventory { - - public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( - entityPlayer, items - ); - - entityPlayer.activeContainer = containerCartographyTable; - - int id = containerCartographyTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container cartography table - * - * @since 0.8.0 - */ - private class ContainerCartographyTableImpl extends ContainerCartography { - - /** - * The player for this cartography table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container cartography table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - inventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); - - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_1; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_1.util.TextHolderUtil; +import net.minecraft.server.v1_16_R1.*; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.16 R1 + * + * @since 0.8.0 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + entityPlayer, items + ); + + entityPlayer.activeContainer = containerCartographyTable; + + int id = containerCartographyTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.8.0 + */ + private class ContainerCartographyTableImpl extends ContainerCartography { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultInventoryField; + + public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory"); + this.resultInventoryField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + @NotNull + @Contract(pure = true) + private IInventory getResultInventory() { + try { + return (IInventory) resultInventoryField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/EnchantingTableInventoryImpl.java b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/EnchantingTableInventoryImpl.java similarity index 94% rename from nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/EnchantingTableInventoryImpl.java rename to nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/EnchantingTableInventoryImpl.java index 901cceb5e..73a9c5f62 100644 --- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/EnchantingTableInventoryImpl.java +++ b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/EnchantingTableInventoryImpl.java @@ -1,194 +1,196 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; -import net.minecraft.server.v1_16_R1.*; -import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryEnchanting; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal enchanting table inventory for 1.16 R1 - * - * @since 0.8.0 - */ -public class EnchantingTableInventoryImpl extends EnchantingTableInventory { - - public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 2) { - throw new IllegalArgumentException( - "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerEnchantmentTable; - - int id = containerEnchantmentTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container enchanting table - * - * @since 0.8.0 - */ - private class ContainerEnchantingTableImpl extends ContainerEnchantTable { - - /** - * The player for this enchanting table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container enchanting table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the enchant slots field - */ - @NotNull - private final Field enchantSlotsField; - - public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots"); - this.enchantSlotsField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - try { - IInventory input = (IInventory) enchantSlotsField.get(this); - - input.setItem(0, CraftItemStack.asNMSCopy(items[0])); - input.setItem(1, CraftItemStack.asNMSCopy(items[1])); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - try { - CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } catch (IllegalAccessException exception) { - exception.printStackTrace(); - } - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_1; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_1.util.TextHolderUtil; +import net.minecraft.server.v1_16_R1.*; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.16 R1 + * + * @since 0.8.0 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerEnchantmentTable; + + int id = containerEnchantmentTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.8.0 + */ + private class ContainerEnchantingTableImpl extends ContainerEnchantTable { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots"); + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + IInventory input = (IInventory) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + } +} diff --git a/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/GrindstoneInventoryImpl.java b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..c349af46c --- /dev/null +++ b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/GrindstoneInventoryImpl.java @@ -0,0 +1,268 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_1; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_1.util.TextHolderUtil; +import net.minecraft.server.v1_16_R1.*; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Internal grindstone inventory for 1.16 R1 + * + * @since 0.8.0 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(entityPlayer); + + entityPlayer.activeContainer = entityPlayer.defaultContainer; + + IChatBaseComponent message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int windowId = containerGrindstone.getWindowId(); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(windowId, Containers.GRINDSTONE, message)); + entityPlayer.activeContainer = containerGrindstone; + entityPlayer.syncInventory(); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.8.0 + */ + private static class ContainerGrindstoneImpl extends ContainerGrindstone { + + /** + * A unique item + */ + @NotNull + private final ItemStack uniqueItem; + + /** + * The field containing the listeners for this container + */ + @NotNull + private final Field listenersField; + + /** + * Creates a new grindstone container + * + * @param entityPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + try { + this.listenersField = Container.class.getDeclaredField("listeners"); + this.listenersField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.inventory, firstSlot.rawSlotIndex, firstSlot.e, firstSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.inventory, secondSlot.rawSlotIndex, secondSlot.e, secondSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.inventory, thirdSlot.rawSlotIndex, thirdSlot.e, thirdSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + + @Override + public ItemStack a(EntityHuman entityHuman, ItemStack itemStack) { + return itemStack; + } + }); + + this.uniqueItem = new ItemStack(Items.COOKIE); + + //to make the item unique, we add a random uuid as nbt to it + UUID uuid = UUID.randomUUID(); + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + + nbtTagCompound.set("uuid", new NBTTagLongArray(new long [] { + uuid.getLeastSignificantBits(), + uuid.getMostSignificantBits() + })); + + this.uniqueItem.setTag(nbtTagCompound); + } + + /** + * Forcefully updates the client state, sending all items, container properties and the held item. + * + * @since 0.10.8 + */ + public void forceUpdate() { + /* + The server will not send the items when they haven't changed, so we will overwrite every item with a unique + item first to ensure the server is going to send our items. + */ + Collections.fill(this.items, this.uniqueItem); + + notifyListeners(); + + List listeners; + + try { + //noinspection unchecked + listeners = (List) listenersField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + for (ICrafting listener : listeners) { + if (!(listener instanceof EntityPlayer)) { + continue; + } + + EntityPlayer player = (EntityPlayer) listener; + + player.e = false; + player.broadcastCarriedItem(); + } + } + + @Override + public ItemStack a(int i, int j, InventoryClickType inventoryclicktype, EntityHuman entityhuman) { + ItemStack itemStack = super.a(i, j, inventoryclicktype, entityhuman); + + //the client predicts the allowed movement of the item, so we broadcast the state again to override it + forceUpdate(); + + return itemStack; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityHuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityHuman) {} + + public int getWindowId() { + return this.windowId; + } + } +} diff --git a/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/MerchantInventoryImpl.java b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/MerchantInventoryImpl.java new file mode 100644 index 000000000..b54526b3e --- /dev/null +++ b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/MerchantInventoryImpl.java @@ -0,0 +1,95 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_1; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.v1_16_R1.EntityPlayer; +import net.minecraft.server.v1_16_R1.MerchantRecipeList; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.16.1 + * + * @since 0.10.1 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantRecipeList offers = new MerchantRecipeList(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.server.v1_16_R1.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.server.v1_16_R1.ItemStack nmsItemB = net.minecraft.server.v1_16_R1.ItemStack.b; + net.minecraft.server.v1_16_R1.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + net.minecraft.server.v1_16_R1.MerchantRecipe merchantOffer = new net.minecraft.server.v1_16_R1.MerchantRecipe( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPrice(entry.getValue()); + + offers.add(merchantOffer); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + entityPlayer.openTrade(getWindowId(entityPlayer), offers, level, experience, true, false); + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.10.1 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.10.1 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } +} diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/SmithingTableInventoryImpl.java b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/SmithingTableInventoryImpl.java similarity index 91% rename from nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/SmithingTableInventoryImpl.java rename to nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/SmithingTableInventoryImpl.java index c9e30f97c..d662e556c 100644 --- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/SmithingTableInventoryImpl.java +++ b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/SmithingTableInventoryImpl.java @@ -1,226 +1,233 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; -import net.minecraft.server.v1_16_R1.*; -import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventorySmithing; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Internal smithing table inventory for 1.16 R1 - * - * @since 0.8.0 - */ -public class SmithingTableInventoryImpl extends SmithingTableInventory { - - public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a stonecutter should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerSmithingTable; - - int id = containerSmithingTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.SMITHING, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); - } - - @Override - public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); - } - - @Override - public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - sendResultItem(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearResultItem(@NotNull Player player) { - sendResultItem(player, ItemStack.b); - } - - @Override - public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { - setCursor(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Sets the cursor of the given player - * - * @param player the player to set the cursor - * @param item the item to set the cursor to - * @since 0.8.0 - */ - private void setCursor(@NotNull Player player, @NotNull ItemStack item) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); - } - - /** - * Sends the result item to the specified player with the given item - * - * @param player the player to send the result item to - * @param item the result item - * @since 0.8.0 - */ - private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container smithing table - * - * @since 0.8.0 - */ - private class ContainerSmithingTableImpl extends ContainerSmithing { - - /** - * The player for this smithing table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container smithing table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - public ContainerSmithingTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, - ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); - - this.player = entityPlayer.getBukkitEntity(); - - repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); - resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventorySmithing(repairInventory, resultInventory) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_1; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_1.util.TextHolderUtil; +import net.minecraft.server.v1_16_R1.*; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.16 R1 + * + * @since 0.8.0 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerSmithingTable; + + int id = containerSmithingTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.SMITHING, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items, null); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.b); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.8.0 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.8.0 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.8.0 + */ + private class ContainerSmithingTableImpl extends ContainerSmithing { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, + ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); + + this.player = entityPlayer.getBukkitEntity(); + + repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(repairInventory, resultInventory) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + } +} diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/StonecutterInventoryImpl.java b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/StonecutterInventoryImpl.java similarity index 94% rename from nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/StonecutterInventoryImpl.java rename to nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/StonecutterInventoryImpl.java index 8c76fb843..aa9889fcd 100644 --- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/StonecutterInventoryImpl.java +++ b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/StonecutterInventoryImpl.java @@ -1,199 +1,201 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; -import net.minecraft.server.v1_16_R1.*; -import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryStonecutter; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal stonecutter inventory for 1.16 R1 - * - * @since 0.8.0 - */ -public class StonecutterInventoryImpl extends StonecutterInventory { - - public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 2) { - throw new IllegalArgumentException( - "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerEnchantmentTable; - - int id = containerEnchantmentTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container enchanting table - * - * @since 0.8.0 - */ - private class ContainerStonecutterImpl extends ContainerStonecutter { - - /** - * The player for this enchanting table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container enchanting table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - /** - * Gets the result inventory - * - * @return the result inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - public IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_1; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_1.util.TextHolderUtil; +import net.minecraft.server.v1_16_R1.*; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.16 R1 + * + * @since 0.8.0 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerEnchantmentTable; + + int id = containerEnchantmentTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.8.0 + */ + private class ContainerStonecutterImpl extends ContainerStonecutter { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultInventoryField; + + public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory"); + this.resultInventoryField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + public IInventory getResultInventory() { + try { + return (IInventory) resultInventoryField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/util/TextHolderUtil.java b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/util/TextHolderUtil.java new file mode 100644 index 000000000..1e805f59b --- /dev/null +++ b/nms/1_16_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_1/util/TextHolderUtil.java @@ -0,0 +1,66 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_1.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.server.v1_16_R1.ChatComponentText; +import net.minecraft.server.v1_16_R1.IChatBaseComponent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.0 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static IChatBaseComponent toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static IChatBaseComponent toComponent(@NotNull StringHolder holder) { + return new ChatComponentText(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static IChatBaseComponent toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(IChatBaseComponent.ChatSerializer.a(holder.asJson())); + } +} diff --git a/nms/1_16_R2/pom.xml b/nms/1_16_2-3/pom.xml similarity index 86% rename from nms/1_16_R2/pom.xml rename to nms/1_16_2-3/pom.xml index f630a7f1e..147afb076 100644 --- a/nms/1_16_R2/pom.xml +++ b/nms/1_16_2-3/pom.xml @@ -5,12 +5,12 @@ IF-parent com.github.stefvanschie.inventoryframework - 0.9.8 + 0.10.18 ../../pom.xml 4.0.0 - 1_16_R2 + 1_16_2-3 true @@ -18,8 +18,8 @@ - org.spigotmc - spigot + io.papermc + paper 1.16.3-R0.1-SNAPSHOT provided diff --git a/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/AnvilInventoryImpl.java b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/AnvilInventoryImpl.java new file mode 100644 index 000000000..83b57373d --- /dev/null +++ b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/AnvilInventoryImpl.java @@ -0,0 +1,386 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.util.TextHolderUtil; +import net.minecraft.server.v1_16_R2.*; +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Internal anvil inventory for 1.16 R2 + * + * @since 0.8.0 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(entityPlayer); + + entityPlayer.activeContainer = entityPlayer.defaultContainer; + + IChatBaseComponent message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(containerId, Containers.ANVIL, message)); + entityPlayer.activeContainer = containerAnvil; + entityPlayer.syncInventory(); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.b); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.8.0 + */ + private class ContainerAnvilImpl extends ContainerAnvil { + + /** + * The index of the result slot + */ + private static final int RESULT_SLOT_INDEX = 2; + + /** + * A unique item + */ + @NotNull + private final ItemStack uniqueItem; + + /** + * The field containing the listeners for this container + */ + @NotNull + private final Field listenersField; + + /** + * Creates a new custom anvil container for the specified player + * + * @param entityPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer, @NotNull IChatBaseComponent title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, + ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); + + this.checkReachable = false; + + try { + //stores all the registered container properties + Field dField = Container.class.getDeclaredField("d"); + dField.setAccessible(true); + + //get rid of the level cost property + ((List) dField.get(this)).clear(); + } catch (NoSuchFieldException | IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'd'", exception); + } + + try { + this.listenersField = Container.class.getDeclaredField("listeners"); + this.listenersField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + //register a new property for the level cost + ContainerProperty levelCost = a(new ContainerProperty() { + private int value; + + @Override + public int get() { + return value; + } + + @Override + public void set(int value) { + this.value = value; + } + + /* + This checks whether there have been any changes, but we want to override the client prediction. This + means the server should be sending the data to the client, even if it didn't change server-side. To + force this, we tell the server the data has always changed. + */ + @Override + public boolean c() { + return true; + } + }); + + levelCost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(RESULT_SLOT_INDEX); + + Slot newSlot = new Slot(originalSlot.inventory, originalSlot.index, originalSlot.e, originalSlot.f) { + @Override + public boolean isAllowed(ItemStack itemStack) { + return true; + } + + @Override + public boolean isAllowed(EntityHuman entityHuman) { + return true; + } + + @Override + public ItemStack a(EntityHuman entityHuman, @NotNull ItemStack itemStack) { + return originalSlot.a(entityHuman, itemStack); + } + }; + + this.slots.set(RESULT_SLOT_INDEX, newSlot); + + this.uniqueItem = new ItemStack(Items.COOKIE); + + //to make the item unique, we add a random uuid as nbt to it + UUID uuid = UUID.randomUUID(); + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + + nbtTagCompound.set("uuid", new NBTTagLongArray(new long [] { + uuid.getLeastSignificantBits(), + uuid.getMostSignificantBits() + })); + + this.uniqueItem.setTag(nbtTagCompound); + } + + @Override + public void a(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + forceUpdate(); + } + + @Override + public void e() {} + + @Override + public void b(EntityHuman entityHuman) {} + + @Override + protected void a(EntityHuman entityHuman, World world, @NotNull IInventory inventory) {} + + @Override + protected ItemStack a(EntityHuman entityHuman, @NotNull ItemStack itemStack) { + return itemStack; + } + + @Override + public ItemStack a(int i, int j, InventoryClickType inventoryclicktype, EntityHuman entityhuman) { + ItemStack itemStack = super.a(i, j, inventoryclicktype, entityhuman); + + //the client predicts the allowed movement of the item, so we broadcast the state again to override it + forceUpdate(); + + return itemStack; + } + + public int getContainerId() { + return this.windowId; + } + + /** + * Forcefully updates the client state, sending all items, container properties and the held item. + * + * @since 0.10.8 + */ + public void forceUpdate() { + /* + The server will not send the items when they haven't changed, so we will overwrite every item with a unique + item first to ensure the server is going to send our items. + */ + Collections.fill(this.items, this.uniqueItem); + + c(); + + List listeners; + + try { + //noinspection unchecked + listeners = (List) listenersField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + for (ICrafting listener : listeners) { + if (!(listener instanceof EntityPlayer)) { + continue; + } + + EntityPlayer player = (EntityPlayer) listener; + + player.e = false; + player.broadcastCarriedItem(); + } + } + } +} diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/BeaconInventoryImpl.java b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/BeaconInventoryImpl.java similarity index 95% rename from nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/BeaconInventoryImpl.java rename to nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/BeaconInventoryImpl.java index 2a8d3cc64..80e5b9a93 100644 --- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/BeaconInventoryImpl.java +++ b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/BeaconInventoryImpl.java @@ -1,178 +1,178 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R2; - -import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; -import net.minecraft.server.v1_16_R2.*; -import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R2.inventory.*; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal beacon inventory for 1.16 R2 - * - * @since 0.8.0 - */ -public class BeaconInventoryImpl extends BeaconInventory { - - public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item); - - entityPlayer.activeContainer = containerBeacon; - - int id = containerBeacon.windowId; - ChatMessage message = new ChatMessage("Beacon"); - - entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message)); - - sendItem(player, item); - } - - @Override - public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - NonNullList items = NonNullList.a( - ItemStack.b, //the first item doesn't count for some reason, so send a dummy item - CraftItemStack.asNMSCopy(item) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container beacon - * - * @since 0.8.0 - */ - private class ContainerBeaconImpl extends ContainerBeacon { - - /** - * The player for this beacon container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container beacon - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the beacon field - */ - @NotNull - private final Field beaconField; - - public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.beaconField = ContainerBeacon.class.getDeclaredField("beacon"); - this.beaconField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - try { - ItemStack itemStack = CraftItemStack.asNMSCopy(item); - - ((IInventory) beaconField.get(this)).setItem(0, itemStack); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - try { - CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.server.v1_16_R2.*; +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R2.inventory.*; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.16 R2 + * + * @since 0.8.0 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item); + + entityPlayer.activeContainer = containerBeacon; + + int id = containerBeacon.windowId; + ChatMessage message = new ChatMessage("Beacon"); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.a( + ItemStack.b, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.8.0 + */ + private class ContainerBeaconImpl extends ContainerBeacon { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.beaconField = ContainerBeacon.class.getDeclaredField("beacon"); + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((IInventory) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + } +} diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/CartographyTableInventoryImpl.java b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/CartographyTableInventoryImpl.java similarity index 94% rename from nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/CartographyTableInventoryImpl.java rename to nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/CartographyTableInventoryImpl.java index b11ac0f23..bf6d8ba59 100644 --- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/CartographyTableInventoryImpl.java +++ b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/CartographyTableInventoryImpl.java @@ -1,196 +1,198 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R2; - -import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; -import net.minecraft.server.v1_16_R2.*; -import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R2.inventory.*; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal cartography table inventory for 1.16 R2 - * - * @since 0.8.0 - */ -public class CartographyTableInventoryImpl extends CartographyTableInventory { - - public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( - entityPlayer, items - ); - - entityPlayer.activeContainer = containerCartographyTable; - - int id = containerCartographyTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container cartography table - * - * @since 0.8.0 - */ - private class ContainerCartographyTableImpl extends ContainerCartography { - - /** - * The player for this cartography table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container cartography table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - inventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); - - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.util.TextHolderUtil; +import net.minecraft.server.v1_16_R2.*; +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R2.inventory.*; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.16 R2 + * + * @since 0.8.0 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + entityPlayer, items + ); + + entityPlayer.activeContainer = containerCartographyTable; + + int id = containerCartographyTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.8.0 + */ + private class ContainerCartographyTableImpl extends ContainerCartography { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultInventoryField; + + public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory"); + this.resultInventoryField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + @NotNull + @Contract(pure = true) + private IInventory getResultInventory() { + try { + return (IInventory) resultInventoryField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/EnchantingTableInventoryImpl.java b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/EnchantingTableInventoryImpl.java similarity index 94% rename from nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/EnchantingTableInventoryImpl.java rename to nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/EnchantingTableInventoryImpl.java index 89145689c..abf9e5393 100644 --- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/EnchantingTableInventoryImpl.java +++ b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/EnchantingTableInventoryImpl.java @@ -1,191 +1,193 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R2; - -import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; -import net.minecraft.server.v1_16_R2.*; -import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R2.inventory.*; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal enchanting table inventory for 1.16 R2 - * - * @since 0.8.0 - */ -public class EnchantingTableInventoryImpl extends EnchantingTableInventory { - - public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 2) { - throw new IllegalArgumentException( - "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerEnchantmentTable; - - int id = containerEnchantmentTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container enchanting table - * - * @since 0.8.0 - */ - private class ContainerEnchantingTableImpl extends ContainerEnchantTable { - - /** - * The player for this enchanting table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container enchanting table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the enchant slots field - */ - @NotNull - private final Field enchantSlotsField; - - public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots"); - this.enchantSlotsField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - try { - IInventory input = (IInventory) enchantSlotsField.get(this); - - input.setItem(0, CraftItemStack.asNMSCopy(items[0])); - input.setItem(1, CraftItemStack.asNMSCopy(items[1])); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - try { - CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } catch (IllegalAccessException exception) { - exception.printStackTrace(); - } - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.util.TextHolderUtil; +import net.minecraft.server.v1_16_R2.*; +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R2.inventory.*; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.16 R2 + * + * @since 0.8.0 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerEnchantmentTable; + + int id = containerEnchantmentTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.8.0 + */ + private class ContainerEnchantingTableImpl extends ContainerEnchantTable { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots"); + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + IInventory input = (IInventory) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + } +} diff --git a/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/GrindstoneInventoryImpl.java b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..0950c0553 --- /dev/null +++ b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/GrindstoneInventoryImpl.java @@ -0,0 +1,271 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.util.TextHolderUtil; +import net.minecraft.server.v1_16_R2.*; +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_16_R2.inventory.*; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Internal grindstone inventory for 1.16 R2 + * + * @since 0.8.0 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(entityPlayer); + + entityPlayer.activeContainer = entityPlayer.defaultContainer; + + IChatBaseComponent message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int windowId = containerGrindstone.getWindowId(); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(windowId, Containers.GRINDSTONE, message)); + entityPlayer.activeContainer = containerGrindstone; + entityPlayer.syncInventory(); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.8.0 + */ + private static class ContainerGrindstoneImpl extends ContainerGrindstone { + + /** + * A unique item + */ + @NotNull + private final ItemStack uniqueItem; + + /** + * The field containing the listeners for this container + */ + @NotNull + private final Field listenersField; + + /** + * Creates a new grindstone container + * + * @param entityPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + try { + this.listenersField = Container.class.getDeclaredField("listeners"); + this.listenersField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.inventory, firstSlot.rawSlotIndex, firstSlot.e, firstSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.inventory, secondSlot.rawSlotIndex, secondSlot.e, secondSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.inventory, thirdSlot.rawSlotIndex, thirdSlot.e, thirdSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + + @Override + public ItemStack a(EntityHuman entityHuman, ItemStack itemStack) { + return itemStack; + } + }); + + this.uniqueItem = new ItemStack(Items.COOKIE); + + //to make the item unique, we add a random uuid as nbt to it + UUID uuid = UUID.randomUUID(); + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + + nbtTagCompound.set("uuid", new NBTTagLongArray(new long [] { + uuid.getLeastSignificantBits(), + uuid.getMostSignificantBits() + })); + + this.uniqueItem.setTag(nbtTagCompound); + } + + /** + * Forcefully updates the client state, sending all items, container properties and the held item. + * + * @since 0.10.8 + */ + public void forceUpdate() { + /* + The server will not send the items when they haven't changed, so we will overwrite every item with a unique + item first to ensure the server is going to send our items. + */ + Collections.fill(this.items, this.uniqueItem); + + notifyListeners(); + + List listeners; + + try { + //noinspection unchecked + listeners = (List) listenersField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + for (ICrafting listener : listeners) { + if (!(listener instanceof EntityPlayer)) { + continue; + } + + EntityPlayer player = (EntityPlayer) listener; + + player.e = false; + player.broadcastCarriedItem(); + } + } + + @Override + public ItemStack a(int i, int j, InventoryClickType inventoryclicktype, EntityHuman entityhuman) { + ItemStack itemStack = super.a(i, j, inventoryclicktype, entityhuman); + + //the client predicts the allowed movement of the item, so we broadcast the state again to override it + forceUpdate(); + + return itemStack; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityHuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityHuman) {} + + public int getWindowId() { + return this.windowId; + } + } +} diff --git a/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/MerchantInventoryImpl.java b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/MerchantInventoryImpl.java new file mode 100644 index 000000000..9401a94d1 --- /dev/null +++ b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/MerchantInventoryImpl.java @@ -0,0 +1,95 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.v1_16_R2.EntityPlayer; +import net.minecraft.server.v1_16_R2.MerchantRecipeList; +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.16.2 - 1.16.3 + * + * @since 0.10.1 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantRecipeList offers = new MerchantRecipeList(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.server.v1_16_R2.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.server.v1_16_R2.ItemStack nmsItemB = net.minecraft.server.v1_16_R2.ItemStack.b; + net.minecraft.server.v1_16_R2.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + net.minecraft.server.v1_16_R2.MerchantRecipe merchantOffer = new net.minecraft.server.v1_16_R2.MerchantRecipe( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPrice(entry.getValue()); + + offers.add(merchantOffer); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + entityPlayer.openTrade(getWindowId(entityPlayer), offers, level, experience, true, false); + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.10.1 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.10.1 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } +} diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/SmithingTableInventoryImpl.java b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/SmithingTableInventoryImpl.java similarity index 91% rename from nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/SmithingTableInventoryImpl.java rename to nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/SmithingTableInventoryImpl.java index 82779d966..18dad0415 100644 --- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/SmithingTableInventoryImpl.java +++ b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/SmithingTableInventoryImpl.java @@ -1,224 +1,231 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R2; - -import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; -import net.minecraft.server.v1_16_R2.*; -import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R2.inventory.*; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Internal smithing table inventory for 1.16 R2 - * - * @since 0.8.0 - */ -public class SmithingTableInventoryImpl extends SmithingTableInventory { - - public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a stonecutter should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerSmithingTable; - - int id = containerSmithingTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.SMITHING, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); - } - - @Override - public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); - } - - @Override - public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - sendResultItem(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearResultItem(@NotNull Player player) { - sendResultItem(player, ItemStack.b); - } - - @Override - public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { - setCursor(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Sets the cursor of the given player - * - * @param player the player to set the cursor - * @param item the item to set the cursor to - * @since 0.8.0 - */ - private void setCursor(@NotNull Player player, @NotNull ItemStack item) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); - } - - /** - * Sends the result item to the specified player with the given item - * - * @param player the player to send the result item to - * @param item the result item - * @since 0.8.0 - */ - private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container smithing table - * - * @since 0.8.0 - */ - private class ContainerSmithingTableImpl extends ContainerSmithing { - - /** - * The player for this smithing table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container smithing table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - public ContainerSmithingTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, - ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); - - this.player = entityPlayer.getBukkitEntity(); - - repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); - resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventorySmithing(containerAccess.getLocation(), repairInventory, - resultInventory) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.util.TextHolderUtil; +import net.minecraft.server.v1_16_R2.*; +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R2.inventory.*; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.16 R2 + * + * @since 0.8.0 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerSmithingTable; + + int id = containerSmithingTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.SMITHING, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items, null); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.b); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.8.0 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.8.0 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.8.0 + */ + private class ContainerSmithingTableImpl extends ContainerSmithing { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, + ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); + + this.player = entityPlayer.getBukkitEntity(); + + repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(containerAccess.getLocation(), repairInventory, + resultInventory) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + } +} diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/StonecutterInventoryImpl.java b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/StonecutterInventoryImpl.java similarity index 94% rename from nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/StonecutterInventoryImpl.java rename to nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/StonecutterInventoryImpl.java index 83d8d88d4..05f40294e 100644 --- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/StonecutterInventoryImpl.java +++ b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/StonecutterInventoryImpl.java @@ -1,196 +1,198 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R2; - -import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; -import net.minecraft.server.v1_16_R2.*; -import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R2.inventory.*; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal stonecutter inventory for 1.16 R2 - * - * @since 0.8.0 - */ -public class StonecutterInventoryImpl extends StonecutterInventory { - - public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 2) { - throw new IllegalArgumentException( - "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerEnchantmentTable; - - int id = containerEnchantmentTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container enchanting table - * - * @since 0.8.0 - */ - private class ContainerStonecutterImpl extends ContainerStonecutter { - - /** - * The player for this enchanting table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container enchanting table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - /** - * Gets the result inventory - * - * @return the result inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - public IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.util.TextHolderUtil; +import net.minecraft.server.v1_16_R2.*; +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R2.inventory.*; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.16 R2 + * + * @since 0.8.0 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerEnchantmentTable; + + int id = containerEnchantmentTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.8.0 + */ + private class ContainerStonecutterImpl extends ContainerStonecutter { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultInventoryField; + + public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory"); + this.resultInventoryField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + public IInventory getResultInventory() { + try { + return (IInventory) resultInventoryField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/util/TextHolderUtil.java b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/util/TextHolderUtil.java new file mode 100644 index 000000000..42b2c9036 --- /dev/null +++ b/nms/1_16_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_2_3/util/TextHolderUtil.java @@ -0,0 +1,66 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_2_3.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.server.v1_16_R2.ChatComponentText; +import net.minecraft.server.v1_16_R2.IChatBaseComponent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.0 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static IChatBaseComponent toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static IChatBaseComponent toComponent(@NotNull StringHolder holder) { + return new ChatComponentText(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static IChatBaseComponent toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(IChatBaseComponent.ChatSerializer.a(holder.asJson())); + } +} diff --git a/nms/1_16_R3/pom.xml b/nms/1_16_4-5/pom.xml similarity index 86% rename from nms/1_16_R3/pom.xml rename to nms/1_16_4-5/pom.xml index 5ddb316fd..8f5722fb5 100644 --- a/nms/1_16_R3/pom.xml +++ b/nms/1_16_4-5/pom.xml @@ -5,30 +5,30 @@ IF-parent com.github.stefvanschie.inventoryframework - 0.9.8 + 0.10.18 ../../pom.xml 4.0.0 - 1_16_R3 + 1_16_4-5 true + + io.papermc + paper + 1.16.4-R0.1-SNAPSHOT + provided + com.github.stefvanschie.inventoryframework abstraction ${project.version} compile - - org.spigotmc - spigot - 1.16.4-R0.1-SNAPSHOT - provided - \ No newline at end of file diff --git a/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/AnvilInventoryImpl.java b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/AnvilInventoryImpl.java new file mode 100644 index 000000000..99f65b47e --- /dev/null +++ b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/AnvilInventoryImpl.java @@ -0,0 +1,386 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_4_5; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.util.TextHolderUtil; +import net.minecraft.server.v1_16_R3.*; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R3.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Internal anvil inventory for 1.16 R3 + * + * @since 0.8.0 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(entityPlayer); + + entityPlayer.activeContainer = entityPlayer.defaultContainer; + + IChatBaseComponent message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(containerId, Containers.ANVIL, message)); + entityPlayer.activeContainer = containerAnvil; + entityPlayer.syncInventory(); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.b); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.8.0 + */ + private class ContainerAnvilImpl extends ContainerAnvil { + + /** + * The index of the result slot + */ + private static final int RESULT_SLOT_INDEX = 2; + + /** + * A unique item + */ + @NotNull + private final ItemStack uniqueItem; + + /** + * The field containing the listeners for this container + */ + @NotNull + private final Field listenersField; + + /** + * Creates a new custom anvil container for the specified player + * + * @param entityPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer, @NotNull IChatBaseComponent title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, + ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); + + this.checkReachable = false; + + try { + //stores all the registered container properties + Field dField = Container.class.getDeclaredField("d"); + dField.setAccessible(true); + + //get rid of the level cost property + ((List) dField.get(this)).clear(); + } catch (NoSuchFieldException | IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'd'", exception); + } + + try { + this.listenersField = Container.class.getDeclaredField("listeners"); + this.listenersField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + //register a new property for the level cost + ContainerProperty levelCost = a(new ContainerProperty() { + private int value; + + @Override + public int get() { + return value; + } + + @Override + public void set(int value) { + this.value = value; + } + + /* + This checks whether there have been any changes, but we want to override the client prediction. This + means the server should be sending the data to the client, even if it didn't change server-side. To + force this, we tell the server the data has always changed. + */ + @Override + public boolean c() { + return true; + } + }); + + levelCost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(RESULT_SLOT_INDEX); + + Slot newSlot = new Slot(originalSlot.inventory, originalSlot.index, originalSlot.e, originalSlot.f) { + @Override + public boolean isAllowed(ItemStack itemStack) { + return true; + } + + @Override + public boolean isAllowed(EntityHuman entityHuman) { + return true; + } + + @Override + public ItemStack a(EntityHuman entityHuman, @NotNull ItemStack itemStack) { + return originalSlot.a(entityHuman, itemStack); + } + }; + + this.slots.set(RESULT_SLOT_INDEX, newSlot); + + this.uniqueItem = new ItemStack(Items.COOKIE); + + //to make the item unique, we add a random uuid as nbt to it + UUID uuid = UUID.randomUUID(); + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + + nbtTagCompound.set("uuid", new NBTTagLongArray(new long [] { + uuid.getLeastSignificantBits(), + uuid.getMostSignificantBits() + })); + + this.uniqueItem.setTag(nbtTagCompound); + } + + @Override + public void a(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + forceUpdate(); + } + + @Override + public void e() {} + + @Override + public void b(EntityHuman entityHuman) {} + + @Override + protected void a(EntityHuman entityHuman, World world, @NotNull IInventory inventory) {} + + @Override + protected ItemStack a(EntityHuman entityHuman, @NotNull ItemStack itemStack) { + return itemStack; + } + + @Override + public ItemStack a(int i, int j, InventoryClickType inventoryclicktype, EntityHuman entityhuman) { + ItemStack itemStack = super.a(i, j, inventoryclicktype, entityhuman); + + //the client predicts the allowed movement of the item, so we broadcast the state again to override it + forceUpdate(); + + return itemStack; + } + + public int getContainerId() { + return this.windowId; + } + + /** + * Forcefully updates the client state, sending all items, container properties and the held item. + * + * @since 0.10.8 + */ + public void forceUpdate() { + /* + The server will not send the items when they haven't changed, so we will overwrite every item with a unique + item first to ensure the server is going to send our items. + */ + Collections.fill(this.items, this.uniqueItem); + + c(); + + List listeners; + + try { + //noinspection unchecked + listeners = (List) listenersField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + for (ICrafting listener : listeners) { + if (!(listener instanceof EntityPlayer)) { + continue; + } + + EntityPlayer player = (EntityPlayer) listener; + + player.e = false; + player.broadcastCarriedItem(); + } + } + } +} diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/BeaconInventoryImpl.java b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/BeaconInventoryImpl.java similarity index 95% rename from nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/BeaconInventoryImpl.java rename to nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/BeaconInventoryImpl.java index b1a433ab3..eaed00847 100644 --- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/BeaconInventoryImpl.java +++ b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/BeaconInventoryImpl.java @@ -1,181 +1,181 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R3; - -import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; -import net.minecraft.server.v1_16_R3.*; -import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryBeacon; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal beacon inventory for 1.16 R3 - * - * @since 0.8.0 - */ -public class BeaconInventoryImpl extends BeaconInventory { - - public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item); - - entityPlayer.activeContainer = containerBeacon; - - int id = containerBeacon.windowId; - ChatMessage message = new ChatMessage("Beacon"); - - entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message)); - - sendItem(player, item); - } - - @Override - public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - NonNullList items = NonNullList.a( - ItemStack.b, //the first item doesn't count for some reason, so send a dummy item - CraftItemStack.asNMSCopy(item) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container beacon - * - * @since 0.8.0 - */ - private class ContainerBeaconImpl extends ContainerBeacon { - - /** - * The player for this beacon container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container beacon - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the beacon field - */ - @NotNull - private final Field beaconField; - - public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.beaconField = ContainerBeacon.class.getDeclaredField("beacon"); - this.beaconField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - try { - ItemStack itemStack = CraftItemStack.asNMSCopy(item); - - ((IInventory) beaconField.get(this)).setItem(0, itemStack); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - try { - CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_4_5; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.server.v1_16_R3.*; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.16 R3 + * + * @since 0.8.0 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item); + + entityPlayer.activeContainer = containerBeacon; + + int id = containerBeacon.windowId; + ChatMessage message = new ChatMessage("Beacon"); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.a( + ItemStack.b, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.8.0 + */ + private class ContainerBeaconImpl extends ContainerBeacon { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.beaconField = ContainerBeacon.class.getDeclaredField("beacon"); + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((IInventory) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + } +} diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/CartographyTableInventoryImpl.java b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/CartographyTableInventoryImpl.java similarity index 94% rename from nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/CartographyTableInventoryImpl.java rename to nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/CartographyTableInventoryImpl.java index e63aba2f3..fef838730 100644 --- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/CartographyTableInventoryImpl.java +++ b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/CartographyTableInventoryImpl.java @@ -1,199 +1,201 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R3; - -import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; -import net.minecraft.server.v1_16_R3.*; -import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryCartography; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal cartography table inventory for 1.16 R3 - * - * @since 0.8.0 - */ -public class CartographyTableInventoryImpl extends CartographyTableInventory { - - public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( - entityPlayer, items - ); - - entityPlayer.activeContainer = containerCartographyTable; - - int id = containerCartographyTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container cartography table - * - * @since 0.8.0 - */ - private class ContainerCartographyTableImpl extends ContainerCartography { - - /** - * The player for this cartography table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container cartography table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - inventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); - - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_4_5; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.util.TextHolderUtil; +import net.minecraft.server.v1_16_R3.*; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.16 R3 + * + * @since 0.8.0 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + entityPlayer, items + ); + + entityPlayer.activeContainer = containerCartographyTable; + + int id = containerCartographyTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.8.0 + */ + private class ContainerCartographyTableImpl extends ContainerCartography { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultInventoryField; + + public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory"); + this.resultInventoryField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + @NotNull + @Contract(pure = true) + private IInventory getResultInventory() { + try { + return (IInventory) resultInventoryField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/EnchantingTableInventoryImpl.java b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/EnchantingTableInventoryImpl.java similarity index 94% rename from nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/EnchantingTableInventoryImpl.java rename to nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/EnchantingTableInventoryImpl.java index 7c3584325..611a64ab5 100644 --- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/EnchantingTableInventoryImpl.java +++ b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/EnchantingTableInventoryImpl.java @@ -1,194 +1,196 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R3; - -import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; -import net.minecraft.server.v1_16_R3.*; -import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryEnchanting; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal enchanting table inventory for 1.16 R3 - * - * @since 0.8.0 - */ -public class EnchantingTableInventoryImpl extends EnchantingTableInventory { - - public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 2) { - throw new IllegalArgumentException( - "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerEnchantmentTable; - - int id = containerEnchantmentTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container enchanting table - * - * @since 0.8.0 - */ - private class ContainerEnchantingTableImpl extends ContainerEnchantTable { - - /** - * The player for this enchanting table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container enchanting table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the enchant slots field - */ - @NotNull - private final Field enchantSlotsField; - - public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots"); - this.enchantSlotsField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - try { - IInventory input = (IInventory) enchantSlotsField.get(this); - - input.setItem(0, CraftItemStack.asNMSCopy(items[0])); - input.setItem(1, CraftItemStack.asNMSCopy(items[1])); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - try { - CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } catch (IllegalAccessException exception) { - exception.printStackTrace(); - } - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_4_5; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.util.TextHolderUtil; +import net.minecraft.server.v1_16_R3.*; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.16 R3 + * + * @since 0.8.0 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerEnchantmentTable; + + int id = containerEnchantmentTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.8.0 + */ + private class ContainerEnchantingTableImpl extends ContainerEnchantTable { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots"); + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + IInventory input = (IInventory) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + } +} diff --git a/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/GrindstoneInventoryImpl.java b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..0ce518a25 --- /dev/null +++ b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/GrindstoneInventoryImpl.java @@ -0,0 +1,268 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_4_5; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.util.TextHolderUtil; +import net.minecraft.server.v1_16_R3.*; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R3.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Internal grindstone inventory for 1.16 R3 + * + * @since 0.8.0 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(entityPlayer); + + entityPlayer.activeContainer = entityPlayer.defaultContainer; + + IChatBaseComponent message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int windowId = containerGrindstone.getWindowId(); + + entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(windowId, Containers.GRINDSTONE, message)); + entityPlayer.activeContainer = containerGrindstone; + entityPlayer.syncInventory(); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.8.0 + */ + private static class ContainerGrindstoneImpl extends ContainerGrindstone { + + /** + * A unique item + */ + @NotNull + private final ItemStack uniqueItem; + + /** + * The field containing the listeners for this container + */ + @NotNull + private final Field listenersField; + + /** + * Creates a new grindstone container + * + * @param entityPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + try { + this.listenersField = Container.class.getDeclaredField("listeners"); + this.listenersField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.inventory, firstSlot.rawSlotIndex, firstSlot.e, firstSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.inventory, secondSlot.rawSlotIndex, secondSlot.e, secondSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.inventory, thirdSlot.rawSlotIndex, thirdSlot.e, thirdSlot.f) { + @Override + public boolean isAllowed(ItemStack stack) { + return true; + } + + @Override + public ItemStack a(EntityHuman entityHuman, ItemStack itemStack) { + return itemStack; + } + }); + + this.uniqueItem = new ItemStack(Items.COOKIE); + + //to make the item unique, we add a random uuid as nbt to it + UUID uuid = UUID.randomUUID(); + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + + nbtTagCompound.set("uuid", new NBTTagLongArray(new long [] { + uuid.getLeastSignificantBits(), + uuid.getMostSignificantBits() + })); + + this.uniqueItem.setTag(nbtTagCompound); + } + + /** + * Forcefully updates the client state, sending all items, container properties and the held item. + * + * @since 0.10.8 + */ + public void forceUpdate() { + /* + The server will not send the items when they haven't changed, so we will overwrite every item with a unique + item first to ensure the server is going to send our items. + */ + Collections.fill(this.items, this.uniqueItem); + + notifyListeners(); + + List listeners; + + try { + //noinspection unchecked + listeners = (List) listenersField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException("Unable to access field 'listeners'", exception); + } + + for (ICrafting listener : listeners) { + if (!(listener instanceof EntityPlayer)) { + continue; + } + + EntityPlayer player = (EntityPlayer) listener; + + player.e = false; + player.broadcastCarriedItem(); + } + } + + @Override + public ItemStack a(int i, int j, InventoryClickType inventoryclicktype, EntityHuman entityhuman) { + ItemStack itemStack = super.a(i, j, inventoryclicktype, entityhuman); + + //the client predicts the allowed movement of the item, so we broadcast the state again to override it + forceUpdate(); + + return itemStack; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityHuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityHuman) {} + + public int getWindowId() { + return this.windowId; + } + } +} diff --git a/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/MerchantInventoryImpl.java b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/MerchantInventoryImpl.java new file mode 100644 index 000000000..1f48e76d2 --- /dev/null +++ b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/MerchantInventoryImpl.java @@ -0,0 +1,95 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_4_5; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.v1_16_R3.EntityPlayer; +import net.minecraft.server.v1_16_R3.MerchantRecipeList; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.16.4 - 1.16.5 + * + * @since 0.10.1 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantRecipeList offers = new MerchantRecipeList(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.server.v1_16_R3.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.server.v1_16_R3.ItemStack nmsItemB = net.minecraft.server.v1_16_R3.ItemStack.b; + net.minecraft.server.v1_16_R3.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + net.minecraft.server.v1_16_R3.MerchantRecipe merchantOffer = new net.minecraft.server.v1_16_R3.MerchantRecipe( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPrice(entry.getValue()); + + offers.add(merchantOffer); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + + entityPlayer.openTrade(getWindowId(entityPlayer), offers, level, experience, true, false); + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.10.1 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.10.1 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } +} diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/SmithingTableInventoryImpl.java b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/SmithingTableInventoryImpl.java similarity index 91% rename from nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/SmithingTableInventoryImpl.java rename to nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/SmithingTableInventoryImpl.java index a9e9e5d60..a1d178405 100644 --- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/SmithingTableInventoryImpl.java +++ b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/SmithingTableInventoryImpl.java @@ -1,227 +1,234 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R3; - -import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; -import net.minecraft.server.v1_16_R3.*; -import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventorySmithing; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Internal smithing table inventory for 1.16 R3 - * - * @since 0.8.0 - */ -public class SmithingTableInventoryImpl extends SmithingTableInventory { - - public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerSmithingTable; - - int id = containerSmithingTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.SMITHING, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); - } - - @Override - public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); - } - - @Override - public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - sendResultItem(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearResultItem(@NotNull Player player) { - sendResultItem(player, ItemStack.b); - } - - @Override - public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { - setCursor(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Sets the cursor of the given player - * - * @param player the player to set the cursor - * @param item the item to set the cursor to - * @since 0.8.0 - */ - private void setCursor(@NotNull Player player, @NotNull ItemStack item) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); - } - - /** - * Sends the result item to the specified player with the given item - * - * @param player the player to send the result item to - * @param item the result item - * @since 0.8.0 - */ - private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container smithing table - * - * @since 0.8.0 - */ - private class ContainerSmithingTableImpl extends ContainerSmithing { - - /** - * The player for this smithing table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container smithing table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - public ContainerSmithingTableImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, - ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); - - this.player = entityPlayer.getBukkitEntity(); - - repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); - resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventorySmithing(containerAccess.getLocation(), repairInventory, - resultInventory) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_4_5; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.util.TextHolderUtil; +import net.minecraft.server.v1_16_R3.*; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.16 R3 + * + * @since 0.8.0 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerSmithingTable; + + int id = containerSmithingTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.SMITHING, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items, null); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]), + CraftItemStack.asNMSCopy(items[2]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.b); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.8.0 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.8.0 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.8.0 + */ + private class ContainerSmithingTableImpl extends ContainerSmithing { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, + ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); + + this.player = entityPlayer.getBukkitEntity(); + + repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(containerAccess.getLocation(), repairInventory, + resultInventory) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + } +} diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/StonecutterInventoryImpl.java b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/StonecutterInventoryImpl.java similarity index 94% rename from nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/StonecutterInventoryImpl.java rename to nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/StonecutterInventoryImpl.java index 2e9db691e..0d44d97c9 100644 --- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/StonecutterInventoryImpl.java +++ b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/StonecutterInventoryImpl.java @@ -1,199 +1,201 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R3; - -import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; -import net.minecraft.server.v1_16_R3.*; -import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryStonecutter; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal stonecutter inventory for 1.16 R3 - * - * @since 0.8.0 - */ -public class StonecutterInventoryImpl extends StonecutterInventory { - - public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 2) { - throw new IllegalArgumentException( - "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerEnchantmentTable; - - int id = containerEnchantmentTable.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container enchanting table - * - * @since 0.8.0 - */ - private class ContainerStonecutterImpl extends ContainerStonecutter { - - /** - * The player for this enchanting table container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container enchanting table - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - /** - * Gets the result inventory - * - * @return the result inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - public IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} +package com.github.stefvanschie.inventoryframework.nms.v1_16_4_5; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.util.TextHolderUtil; +import net.minecraft.server.v1_16_R3.*; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.16 R3 + * + * @since 0.8.0 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + EntityPlayer entityPlayer = getEntityPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items); + + entityPlayer.activeContainer = containerEnchantmentTable; + + int id = containerEnchantmentTable.windowId; + IChatBaseComponent message = TextHolderUtil.toComponent(title); + PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message); + + entityPlayer.playerConnection.sendPacket(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.a( + ItemStack.b, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + EntityPlayer entityPlayer = getEntityPlayer(player); + + getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); + } + + /** + * Gets the window id for the inventory view the player currently has open + * + * @param entityPlayer the player to get the window id for + * @return the window id + * @since 0.8.0 + */ + @Contract(pure = true) + private int getWindowId(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.activeContainer.windowId; + } + + /** + * Gets the player connection for the specified player + * + * @param entityPlayer the player to get the player connection from + * @return the player connection + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { + return entityPlayer.playerConnection; + } + + /** + * Gets the entity player associated to this player + * + * @param player the player to get the entity player from + * @return the entity player + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + private EntityPlayer getEntityPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.8.0 + */ + private class ContainerStonecutterImpl extends ContainerStonecutter { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultInventoryField; + + public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); + + this.player = entityPlayer.getBukkitEntity(); + + try { + this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory"); + this.resultInventoryField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + inventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean canUse(@Nullable EntityHuman entityhuman) { + return true; + } + + @Override + public void a(IInventory inventory) {} + + @Override + public void b(EntityHuman entityhuman) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.8.0 + */ + @NotNull + @Contract(pure = true) + public IInventory getResultInventory() { + try { + return (IInventory) resultInventoryField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/util/TextHolderUtil.java b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/util/TextHolderUtil.java new file mode 100644 index 000000000..506d10d9b --- /dev/null +++ b/nms/1_16_4-5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_4_5/util/TextHolderUtil.java @@ -0,0 +1,66 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_16_4_5.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.server.v1_16_R3.ChatComponentText; +import net.minecraft.server.v1_16_R3.IChatBaseComponent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.0 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static IChatBaseComponent toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static IChatBaseComponent toComponent(@NotNull StringHolder holder) { + return new ChatComponentText(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static IChatBaseComponent toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(IChatBaseComponent.ChatSerializer.a(holder.asJson())); + } +} diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/AnvilInventoryImpl.java b/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/AnvilInventoryImpl.java deleted file mode 100644 index 5da3e3f29..000000000 --- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/AnvilInventoryImpl.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; -import net.minecraft.server.v1_16_R1.*; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryAnvil; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Internal anvil inventory for 1.16 R1 - * - * @since 0.8.0 - */ -public class AnvilInventoryImpl extends AnvilInventory { - - public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerAnvil; - - int id = containerAnvil.windowId; - ChatMessage message = new ChatMessage(title); - - entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.ANVIL, message)); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); - } - - @Override - public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); - } - - @Override - public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - sendResultItem(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearResultItem(@NotNull Player player) { - sendResultItem(player, ItemStack.b); - } - - @Override - public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { - setCursor(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Sets the cursor of the given player - * - * @param player the player to set the cursor - * @param item the item to set the cursor to - * @since 0.8.0 - */ - private void setCursor(@NotNull Player player, @NotNull ItemStack item) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); - } - - /** - * Sends the result item to the specified player with the given item - * - * @param player the player to send the result item to - * @param item the result item - * @since 0.8.0 - */ - private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container anvil for responding to item renaming - * - * @since 0.8.0 - */ - private class ContainerAnvilImpl extends ContainerAnvil { - - /** - * The player for whom this anvil container is - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container anvil - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Creates a new custom anvil container for the specified player - * - * @param entityPlayer the player for who this anvil container is - * @since 0.8.0 - */ - public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, - ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); - - this.player = entityPlayer.getBukkitEntity(); - - repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); - resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - Location location = containerAccess.getLocation(); - CraftInventory inventory = new CraftInventoryAnvil(location, repairInventory, resultInventory, - this) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Override - public void a(@Nullable String name) { - text = name == null ? "" : name; - - sendResultItem(player, resultInventory.getItem(0)); - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - } -} diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/GrindstoneInventoryImpl.java b/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/GrindstoneInventoryImpl.java deleted file mode 100644 index 291db39c1..000000000 --- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/GrindstoneInventoryImpl.java +++ /dev/null @@ -1,227 +0,0 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R1; - -import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; -import net.minecraft.server.v1_16_R1.*; -import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryGrindstone; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal grindstone inventory for 1.16 R1 - * - * @since 0.8.0 - */ -public class GrindstoneInventoryImpl extends GrindstoneInventory { - - public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerGrindstone; - - int id = containerGrindstone.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.GRINDSTONE, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container grindstone - * - * @since 0.8.0 - */ - private class ContainerGrindstoneImpl extends ContainerGrindstone { - - /** - * The player for this grindstone container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container grindstone - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the craft inventory field - */ - @NotNull - private final Field craftInventoryField; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.craftInventoryField = ContainerGrindstone.class.getDeclaredField("craftInventory"); - this.craftInventoryField.setAccessible(true); - - this.resultInventoryField = ContainerGrindstone.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - getCraftInventory().setItem(0, CraftItemStack.asNMSCopy(items[0])); - getCraftInventory().setItem(1, CraftItemStack.asNMSCopy(items[1])); - - getResultInventory().setItem(2, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryGrindstone(getCraftInventory(), getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - /** - * Gets the craft inventory - * - * @return the craft inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private IInventory getCraftInventory() { - try { - return (IInventory) craftInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - /** - * Gets the result inventory - * - * @return the result inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/AnvilInventoryImpl.java b/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/AnvilInventoryImpl.java deleted file mode 100644 index b0fc4bd74..000000000 --- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/AnvilInventoryImpl.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R2; - -import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; -import net.minecraft.server.v1_16_R2.*; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftInventoryAnvil; -import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Internal anvil inventory for 1.16 R2 - * - * @since 0.8.0 - */ -public class AnvilInventoryImpl extends AnvilInventory { - - public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerAnvil; - - int id = containerAnvil.windowId; - ChatMessage message = new ChatMessage(title); - - entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.ANVIL, message)); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); - } - - @Override - public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); - } - - @Override - public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - sendResultItem(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearResultItem(@NotNull Player player) { - sendResultItem(player, ItemStack.b); - } - - @Override - public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { - setCursor(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Sets the cursor of the given player - * - * @param player the player to set the cursor - * @param item the item to set the cursor to - * @since 0.8.0 - */ - private void setCursor(@NotNull Player player, @NotNull ItemStack item) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); - } - - /** - * Sends the result item to the specified player with the given item - * - * @param player the player to send the result item to - * @param item the result item - * @since 0.8.0 - */ - private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container anvil for responding to item renaming - * - * @since 0.8.0 - */ - private class ContainerAnvilImpl extends ContainerAnvil { - - /** - * The player for whom this anvil container is - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container anvil - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Creates a new custom anvil container for the specified player - * - * @param entityPlayer the player for who this anvil container is - * @since 0.8.0 - */ - public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, - ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); - - this.player = entityPlayer.getBukkitEntity(); - - repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); - resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - Location location = containerAccess.getLocation(); - CraftInventory inventory = new CraftInventoryAnvil(location, repairInventory, resultInventory, - this) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Override - public void a(@Nullable String name) { - text = name == null ? "" : name; - - sendResultItem(player, resultInventory.getItem(0)); - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - } -} diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/GrindstoneInventoryImpl.java b/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/GrindstoneInventoryImpl.java deleted file mode 100644 index 48bf2f02e..000000000 --- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/GrindstoneInventoryImpl.java +++ /dev/null @@ -1,224 +0,0 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R2; - -import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; -import net.minecraft.server.v1_16_R2.*; -import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R2.inventory.*; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal grindstone inventory for 1.16 R2 - * - * @since 0.8.0 - */ -public class GrindstoneInventoryImpl extends GrindstoneInventory { - - public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerGrindstone; - - int id = containerGrindstone.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.GRINDSTONE, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container grindstone - * - * @since 0.8.0 - */ - private class ContainerGrindstoneImpl extends ContainerGrindstone { - - /** - * The player for this grindstone container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container grindstone - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the craft inventory field - */ - @NotNull - private final Field craftInventoryField; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.craftInventoryField = ContainerGrindstone.class.getDeclaredField("craftInventory"); - this.craftInventoryField.setAccessible(true); - - this.resultInventoryField = ContainerGrindstone.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - getCraftInventory().setItem(0, CraftItemStack.asNMSCopy(items[0])); - getCraftInventory().setItem(1, CraftItemStack.asNMSCopy(items[1])); - - getResultInventory().setItem(2, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryGrindstone(getCraftInventory(), getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - /** - * Gets the craft inventory - * - * @return the craft inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private IInventory getCraftInventory() { - try { - return (IInventory) craftInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - /** - * Gets the result inventory - * - * @return the result inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/AnvilInventoryImpl.java b/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/AnvilInventoryImpl.java deleted file mode 100644 index 636d6ce51..000000000 --- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/AnvilInventoryImpl.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R3; - -import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; -import net.minecraft.server.v1_16_R3.*; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryAnvil; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Internal anvil inventory for 1.16 R3 - * - * @since 0.8.0 - */ -public class AnvilInventoryImpl extends AnvilInventory { - - public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerAnvil; - - int id = containerAnvil.windowId; - ChatMessage message = new ChatMessage(title); - - entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.ANVIL, message)); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem)); - } - - @Override - public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - ItemStack nmsItem = CraftItemStack.asNMSCopy(item); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem)); - } - - @Override - public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { - sendResultItem(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearResultItem(@NotNull Player player) { - sendResultItem(player, ItemStack.b); - } - - @Override - public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { - setCursor(player, CraftItemStack.asNMSCopy(item)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Sets the cursor of the given player - * - * @param player the player to set the cursor - * @param item the item to set the cursor to - * @since 0.8.0 - */ - private void setCursor(@NotNull Player player, @NotNull ItemStack item) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item)); - } - - /** - * Sends the result item to the specified player with the given item - * - * @param player the player to send the result item to - * @param item the result item - * @since 0.8.0 - */ - private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container anvil for responding to item renaming - * - * @since 0.8.0 - */ - private class ContainerAnvilImpl extends ContainerAnvil { - - /** - * The player for whom this anvil container is - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container anvil - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Creates a new custom anvil container for the specified player - * - * @param entityPlayer the player for who this anvil container is - * @since 0.8.0 - */ - public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory, - ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0))); - - this.player = entityPlayer.getBukkitEntity(); - - repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0])); - repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1])); - resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - Location location = containerAccess.getLocation(); - CraftInventory inventory = new CraftInventoryAnvil(location, repairInventory, resultInventory, - this) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Override - public void a(@Nullable String name) { - text = name == null ? "" : name; - - sendResultItem(player, resultInventory.getItem(0)); - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - } -} diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/GrindstoneInventoryImpl.java b/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/GrindstoneInventoryImpl.java deleted file mode 100644 index 140cf4d7b..000000000 --- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/GrindstoneInventoryImpl.java +++ /dev/null @@ -1,227 +0,0 @@ -package com.github.stefvanschie.inventoryframework.nms.v1_16_R3; - -import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; -import net.minecraft.server.v1_16_R3.*; -import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryGrindstone; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; - -/** - * Internal grindstone inventory for 1.16 R3 - * - * @since 0.8.0 - */ -public class GrindstoneInventoryImpl extends GrindstoneInventory { - - public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { - super(inventoryHolder); - } - - @Override - public void openInventory(@NotNull Player player, @NotNull String title, - @Nullable org.bukkit.inventory.ItemStack[] items) { - int itemAmount = items.length; - - if (itemAmount != 3) { - throw new IllegalArgumentException( - "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" - ); - } - - EntityPlayer entityPlayer = getEntityPlayer(player); - ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer, items); - - entityPlayer.activeContainer = containerGrindstone; - - int id = containerGrindstone.windowId; - ChatMessage message = new ChatMessage(title); - PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.GRINDSTONE, message); - - entityPlayer.playerConnection.sendPacket(packet); - - sendItems(player, items); - } - - @Override - public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { - NonNullList nmsItems = NonNullList.a( - ItemStack.b, - CraftItemStack.asNMSCopy(items[0]), - CraftItemStack.asNMSCopy(items[1]), - CraftItemStack.asNMSCopy(items[2]) - ); - - EntityPlayer entityPlayer = getEntityPlayer(player); - - getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems)); - } - - @Override - public void clearCursor(@NotNull Player player) { - getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b)); - } - - /** - * Gets the window id for the inventory view the player currently has open - * - * @param entityPlayer the player to get the window id for - * @return the window id - * @since 0.8.0 - */ - @Contract(pure = true) - private int getWindowId(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.activeContainer.windowId; - } - - /** - * Gets the player connection for the specified player - * - * @param entityPlayer the player to get the player connection from - * @return the player connection - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) { - return entityPlayer.playerConnection; - } - - /** - * Gets the entity player associated to this player - * - * @param player the player to get the entity player from - * @return the entity player - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private EntityPlayer getEntityPlayer(@NotNull Player player) { - return ((CraftPlayer) player).getHandle(); - } - - /** - * A custom container grindstone - * - * @since 0.8.0 - */ - private class ContainerGrindstoneImpl extends ContainerGrindstone { - - /** - * The player for this grindstone container - */ - @NotNull - private final Player player; - - /** - * The internal bukkit entity for this container grindstone - */ - @Nullable - private CraftInventoryView bukkitEntity; - - /** - * Field for accessing the craft inventory field - */ - @NotNull - private final Field craftInventoryField; - - /** - * Field for accessing the result inventory field - */ - @NotNull - private final Field resultInventoryField; - - public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer, - @Nullable org.bukkit.inventory.ItemStack[] items) { - super(entityPlayer.nextContainerCounter(), entityPlayer.inventory); - - this.player = entityPlayer.getBukkitEntity(); - - try { - this.craftInventoryField = ContainerGrindstone.class.getDeclaredField("craftInventory"); - this.craftInventoryField.setAccessible(true); - - this.resultInventoryField = ContainerGrindstone.class.getDeclaredField("resultInventory"); - this.resultInventoryField.setAccessible(true); - } catch (NoSuchFieldException exception) { - throw new RuntimeException(exception); - } - - getCraftInventory().setItem(0, CraftItemStack.asNMSCopy(items[0])); - getCraftInventory().setItem(1, CraftItemStack.asNMSCopy(items[1])); - - getResultInventory().setItem(2, CraftItemStack.asNMSCopy(items[2])); - } - - @NotNull - @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - CraftInventory inventory = new CraftInventoryGrindstone(getCraftInventory(), getResultInventory()) { - @NotNull - @Contract(pure = true) - @Override - public InventoryHolder getHolder() { - return inventoryHolder; - } - }; - - bukkitEntity = new CraftInventoryView(player, inventory, this); - } - - return bukkitEntity; - } - - @Contract(pure = true, value = "_ -> true") - @Override - public boolean canUse(@Nullable EntityHuman entityhuman) { - return true; - } - - @Override - public void a(IInventory inventory) {} - - @Override - public void b(EntityHuman entityhuman) {} - - /** - * Gets the craft inventory - * - * @return the craft inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private IInventory getCraftInventory() { - try { - return (IInventory) craftInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - - /** - * Gets the result inventory - * - * @return the result inventory - * @since 0.8.0 - */ - @NotNull - @Contract(pure = true) - private IInventory getResultInventory() { - try { - return (IInventory) resultInventoryField.get(this); - } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); - } - } - } -} diff --git a/nms/1_17_0/pom.xml b/nms/1_17_0/pom.xml new file mode 100644 index 000000000..9ed5e1571 --- /dev/null +++ b/nms/1_17_0/pom.xml @@ -0,0 +1,59 @@ + + + + IF-parent + com.github.stefvanschie.inventoryframework + 0.10.18 + ../../pom.xml + + 4.0.0 + + 1_17_0 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.17-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + + \ No newline at end of file diff --git a/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/AnvilInventoryImpl.java b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/AnvilInventoryImpl.java new file mode 100644 index 000000000..48e5975e0 --- /dev/null +++ b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/AnvilInventoryImpl.java @@ -0,0 +1,307 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_0; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_17_0.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.ContainerSynchronizer; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal anvil inventory for 1.17 R1 + * + * @since 0.9.9 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetContentPacket(containerId, nmsItems)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ClientboundContainerSetSlotPacket packet = new ClientboundContainerSetSlotPacket(-1, -1, ItemStack.EMPTY); + + getPlayerConnection(getServerPlayer(player)).send(packet); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.9.9 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + getPlayerConnection(getServerPlayer(player)).send(new ClientboundContainerSetSlotPacket(-1, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.9.9 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.9.9 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.9.9 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.9.9 + */ + @SuppressWarnings("JavaReflectionMemberAccess") + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * The index of the result slot + */ + private static final int RESULT_SLOT_INDEX = 2; + + /** + * A field that represents the synchronizer field + */ + @NotNull + private final Field synchronizerField; + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(RESULT_SLOT_INDEX); + + Slot newSlot = new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }; + + this.slots.set(RESULT_SLOT_INDEX, newSlot); + + try { + this.synchronizerField = AbstractContainerMenu.class.getDeclaredField("v"); + } catch (NoSuchFieldException exception) { + throw new RuntimeException("Unable to access field", exception); + } + } + + @Override + public void setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + ContainerSynchronizer synchronizer; + + try { + this.synchronizerField.setAccessible(true); + + synchronizer = (ContainerSynchronizer) this.synchronizerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException("Unable to access field", exception); + } + + synchronizer.sendSlotChange(this, RESULT_SLOT_INDEX, getSlot(RESULT_SLOT_INDEX).getItem()); + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/BeaconInventoryImpl.java b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/BeaconInventoryImpl.java new file mode 100644 index 000000000..38d644489 --- /dev/null +++ b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/BeaconInventoryImpl.java @@ -0,0 +1,195 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_0; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.17 R1 + * + * @since 0.9.9 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + TranslatableComponent message = new TranslatableComponent("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, message)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetContentPacket(containerId, items)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ClientboundContainerSetSlotPacket packet = new ClientboundContainerSetSlotPacket(-1, -1, ItemStack.EMPTY); + + getPlayerConnection(getServerPlayer(player)).send(packet); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.9.9 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.9.9 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/CartographyTableInventoryImpl.java b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..aa3d0c3fa --- /dev/null +++ b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/CartographyTableInventoryImpl.java @@ -0,0 +1,209 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_0; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_0.util.TextHolderUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_17_0.util.CustomInventoryUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.17 R1 + * + * @since 0.9.9 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetContentPacket(containerId, nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ClientboundContainerSetSlotPacket packet = new ClientboundContainerSetSlotPacket(-1, -1, ItemStack.EMPTY); + + getPlayerConnection(getServerPlayer(player)).send(packet); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.9.9 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.9.9 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/EnchantingTableInventoryImpl.java b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..891ee33b5 --- /dev/null +++ b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/EnchantingTableInventoryImpl.java @@ -0,0 +1,209 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_0; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.17 R1 + * + * @since 0.9.9 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetContentPacket(containerId, nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ClientboundContainerSetSlotPacket packet = new ClientboundContainerSetSlotPacket(-1, -1, ItemStack.EMPTY); + + getPlayerConnection(getServerPlayer(player)).send(packet); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.9.9 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.9.9 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/GrindstoneInventoryImpl.java b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..cc6f1af40 --- /dev/null +++ b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/GrindstoneInventoryImpl.java @@ -0,0 +1,196 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_0; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_0.util.TextHolderUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_17_0.util.CustomInventoryUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.event.CraftEventFactory; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.17 R1 + * + * @since 0.9.9 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetContentPacket(containerId, nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ClientboundContainerSetSlotPacket packet = new ClientboundContainerSetSlotPacket(-1, -1, ItemStack.EMPTY); + + getPlayerConnection(getServerPlayer(player)).send(packet); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.9.9 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.9.9 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.9.9 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/MerchantInventoryImpl.java b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/MerchantInventoryImpl.java new file mode 100644 index 000000000..c39cb36e2 --- /dev/null +++ b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_0; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.17.0 + * + * @since 0.10.1 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.1 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.1 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/SmithingTableInventoryImpl.java b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..80045c6ff --- /dev/null +++ b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/SmithingTableInventoryImpl.java @@ -0,0 +1,245 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_0; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_0.util.TextHolderUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_17_0.util.CustomInventoryUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.17 R1 + * + * @since 0.9.9 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerSmithingTable; + + int id = containerSmithingTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.SMITHING, message)); + + sendItems(player, items, null); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetContentPacket(containerId, nmsItems)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ClientboundContainerSetSlotPacket packet = new ClientboundContainerSetSlotPacket(-1, -1, ItemStack.EMPTY); + + getPlayerConnection(getServerPlayer(player)).send(packet); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.9.9 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + getPlayerConnection(getServerPlayer(player)).send(new ClientboundContainerSetSlotPacket(-1, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.9.9 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.9.9 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.9.9 + */ + private class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.player = serverPlayer.getBukkitEntity(); + + inputSlots.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inputSlots.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultSlots.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(access.getLocation(), inputSlots, resultSlots) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + } +} diff --git a/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/StonecutterInventoryImpl.java b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/StonecutterInventoryImpl.java new file mode 100644 index 000000000..7525c3764 --- /dev/null +++ b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/StonecutterInventoryImpl.java @@ -0,0 +1,215 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_0; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.17 R1 + * + * @since 0.9.9 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetContentPacket(containerId, nmsItems)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ClientboundContainerSetSlotPacket packet = new ClientboundContainerSetSlotPacket(-1, -1, ItemStack.EMPTY); + + getPlayerConnection(getServerPlayer(player)).send(packet); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.9.9 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.9.9 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/util/CustomInventoryUtil.java b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/util/CustomInventoryUtil.java new file mode 100644 index 000000000..6b5366b62 --- /dev/null +++ b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_0.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.9.9 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.9.9 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/util/TextHolderUtil.java b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/util/TextHolderUtil.java new file mode 100644 index 000000000..30dd41d1e --- /dev/null +++ b/nms/1_17_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_0/util/TextHolderUtil.java @@ -0,0 +1,66 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_0.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.0 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return new TextComponent(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_17_1/pom.xml b/nms/1_17_1/pom.xml new file mode 100644 index 000000000..76b1958bb --- /dev/null +++ b/nms/1_17_1/pom.xml @@ -0,0 +1,59 @@ + + + + IF-parent + com.github.stefvanschie.inventoryframework + 0.10.18 + ../../pom.xml + + 4.0.0 + + 1_17_1 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.17.1-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + + \ No newline at end of file diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/AnvilInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/AnvilInventoryImpl.java new file mode 100644 index 000000000..8c93a9ec9 --- /dev/null +++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/AnvilInventoryImpl.java @@ -0,0 +1,283 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_1; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.17 R1 + * + * @since 0.10.0 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.0 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.0 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.0 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.0 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.0 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public void setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/BeaconInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/BeaconInventoryImpl.java new file mode 100644 index 000000000..fa1022b40 --- /dev/null +++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/BeaconInventoryImpl.java @@ -0,0 +1,199 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_1; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.17 R1 + * + * @since 0.10.0 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + TranslatableComponent message = new TranslatableComponent("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, message)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.0 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.0 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/CartographyTableInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..7a57a64e1 --- /dev/null +++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/CartographyTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_1; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.17 R1 + * + * @since 0.10.0 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.0 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.0 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/EnchantingTableInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..e1cc97b5f --- /dev/null +++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/EnchantingTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_1; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.17 R1 + * + * @since 0.10.0 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.0 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.0 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/GrindstoneInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..0bc3de75d --- /dev/null +++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/GrindstoneInventoryImpl.java @@ -0,0 +1,205 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_1; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.17 R1 + * + * @since 0.10.0 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.17.1"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.0 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.0 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.0 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/MerchantInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/MerchantInventoryImpl.java new file mode 100644 index 000000000..27c54f5e0 --- /dev/null +++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_1; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.17.1 + * + * @since 0.10.1 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.1 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.1 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/SmithingTableInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..ae263134c --- /dev/null +++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/SmithingTableInventoryImpl.java @@ -0,0 +1,255 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_1; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.17 R1 + * + * @since 0.10.0 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerSmithingTable; + + int id = containerSmithingTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.SMITHING, message)); + + sendItems(player, items, player.getItemOnCursor()); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.0 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.0 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.0 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.0 + */ + private class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.player = serverPlayer.getBukkitEntity(); + + inputSlots.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inputSlots.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultSlots.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(access.getLocation(), inputSlots, resultSlots) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + } +} diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/StonecutterInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/StonecutterInventoryImpl.java new file mode 100644 index 000000000..564cb736f --- /dev/null +++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/StonecutterInventoryImpl.java @@ -0,0 +1,219 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_1; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.17 R1 + * + * @since 0.10.0 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.0 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.0 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/CustomInventoryUtil.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/CustomInventoryUtil.java new file mode 100644 index 000000000..4959cb8b6 --- /dev/null +++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_1.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.0 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/TextHolderUtil.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/TextHolderUtil.java new file mode 100644 index 000000000..9615b0b52 --- /dev/null +++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/TextHolderUtil.java @@ -0,0 +1,66 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_17_1.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.0 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return new TextComponent(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.0 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_18_0/pom.xml b/nms/1_18_0/pom.xml new file mode 100644 index 000000000..26911cff2 --- /dev/null +++ b/nms/1_18_0/pom.xml @@ -0,0 +1,59 @@ + + + + IF-parent + com.github.stefvanschie.inventoryframework + 0.10.18 + ../../pom.xml + + 4.0.0 + + 1_18_0 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.18-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + + \ No newline at end of file diff --git a/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/AnvilInventoryImpl.java b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/AnvilInventoryImpl.java new file mode 100644 index 000000000..4fb3e2bda --- /dev/null +++ b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/AnvilInventoryImpl.java @@ -0,0 +1,283 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_0; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_0.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.18.0 + * + * @since 0.10.4 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.4 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.4 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.4 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.4 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public void setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/BeaconInventoryImpl.java b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/BeaconInventoryImpl.java new file mode 100644 index 000000000..fd6a1ff4d --- /dev/null +++ b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/BeaconInventoryImpl.java @@ -0,0 +1,199 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_0; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.18.0 + * + * @since 0.10.4 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + TranslatableComponent message = new TranslatableComponent("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, message)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.4 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/CartographyTableInventoryImpl.java b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..0f4671deb --- /dev/null +++ b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/CartographyTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_0; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.18.0 + * + * @since 0.10.4 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.4 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/EnchantingTableInventoryImpl.java b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..61c34b4af --- /dev/null +++ b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/EnchantingTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_0; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.18.0 + * + * @since 0.10.4 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.4 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/GrindstoneInventoryImpl.java b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..771ccf841 --- /dev/null +++ b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/GrindstoneInventoryImpl.java @@ -0,0 +1,205 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_0; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.18.0 + * + * @since 0.10.4 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.17.1"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.4 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.4 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/MerchantInventoryImpl.java b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/MerchantInventoryImpl.java new file mode 100644 index 000000000..fe2026e73 --- /dev/null +++ b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_0; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.18.0 + * + * @since 0.10.4 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/SmithingTableInventoryImpl.java b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..c8fd9cf7f --- /dev/null +++ b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/SmithingTableInventoryImpl.java @@ -0,0 +1,255 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_0; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_0.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.18.0 + * + * @since 0.10.4 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerSmithingTable; + + int id = containerSmithingTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.SMITHING, message)); + + sendItems(player, items, player.getItemOnCursor()); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.4 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.4 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.4 + */ + private class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.player = serverPlayer.getBukkitEntity(); + + inputSlots.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inputSlots.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultSlots.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(access.getLocation(), inputSlots, resultSlots) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + } +} diff --git a/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/StonecutterInventoryImpl.java b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/StonecutterInventoryImpl.java new file mode 100644 index 000000000..c2f446f73 --- /dev/null +++ b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/StonecutterInventoryImpl.java @@ -0,0 +1,219 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_0; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.18.0 + * + * @since 0.10.4 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.4 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/util/CustomInventoryUtil.java b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/util/CustomInventoryUtil.java new file mode 100644 index 000000000..185262d0b --- /dev/null +++ b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_0.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.4 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/util/TextHolderUtil.java b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/util/TextHolderUtil.java new file mode 100644 index 000000000..6f2e5e3b2 --- /dev/null +++ b/nms/1_18_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_0/util/TextHolderUtil.java @@ -0,0 +1,66 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_0.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.4 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return new TextComponent(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_18_1/pom.xml b/nms/1_18_1/pom.xml new file mode 100644 index 000000000..cfa853090 --- /dev/null +++ b/nms/1_18_1/pom.xml @@ -0,0 +1,59 @@ + + + + IF-parent + com.github.stefvanschie.inventoryframework + 0.10.18 + ../../pom.xml + + 4.0.0 + + 1_18_1 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.18.1-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + + \ No newline at end of file diff --git a/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/AnvilInventoryImpl.java b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/AnvilInventoryImpl.java new file mode 100644 index 000000000..143dca697 --- /dev/null +++ b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/AnvilInventoryImpl.java @@ -0,0 +1,283 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_1; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_1.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.18.1 + * + * @since 0.10.4 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.4 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.4 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.4 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.4 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public void setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/BeaconInventoryImpl.java b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/BeaconInventoryImpl.java new file mode 100644 index 000000000..52f9cb4ae --- /dev/null +++ b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/BeaconInventoryImpl.java @@ -0,0 +1,199 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_1; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.18.1 + * + * @since 0.10.4 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + TranslatableComponent message = new TranslatableComponent("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, message)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.4 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/CartographyTableInventoryImpl.java b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..48ce90a11 --- /dev/null +++ b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/CartographyTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_1; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.18.1 + * + * @since 0.10.4 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.4 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/EnchantingTableInventoryImpl.java b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..765c9bf28 --- /dev/null +++ b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/EnchantingTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_1; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.18.1 + * + * @since 0.10.4 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.4 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/GrindstoneInventoryImpl.java b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..f8b13b2f9 --- /dev/null +++ b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/GrindstoneInventoryImpl.java @@ -0,0 +1,205 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_1; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.18.1 + * + * @since 0.10.4 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.17.1"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.4 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.4 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/MerchantInventoryImpl.java b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/MerchantInventoryImpl.java new file mode 100644 index 000000000..fabf4cfac --- /dev/null +++ b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_1; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.18.1 + * + * @since 0.10.4 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/SmithingTableInventoryImpl.java b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..0de583897 --- /dev/null +++ b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/SmithingTableInventoryImpl.java @@ -0,0 +1,255 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_1; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_1.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.18.1 + * + * @since 0.10.4 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerSmithingTable; + + int id = containerSmithingTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.SMITHING, message)); + + sendItems(player, items, player.getItemOnCursor()); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.4 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.4 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.4 + */ + private class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.player = serverPlayer.getBukkitEntity(); + + inputSlots.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inputSlots.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultSlots.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(access.getLocation(), inputSlots, resultSlots) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + } +} diff --git a/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/StonecutterInventoryImpl.java b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/StonecutterInventoryImpl.java new file mode 100644 index 000000000..cba4f6200 --- /dev/null +++ b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/StonecutterInventoryImpl.java @@ -0,0 +1,219 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_1; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.18.1 + * + * @since 0.10.4 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.4 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.4 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/util/CustomInventoryUtil.java b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/util/CustomInventoryUtil.java new file mode 100644 index 000000000..f4edc2601 --- /dev/null +++ b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_1.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.4 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/util/TextHolderUtil.java b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/util/TextHolderUtil.java new file mode 100644 index 000000000..3e9e8741b --- /dev/null +++ b/nms/1_18_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_1/util/TextHolderUtil.java @@ -0,0 +1,66 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_1.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.4 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return new TextComponent(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.4 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_18_2/pom.xml b/nms/1_18_2/pom.xml new file mode 100644 index 000000000..09e121568 --- /dev/null +++ b/nms/1_18_2/pom.xml @@ -0,0 +1,59 @@ + + + + IF-parent + com.github.stefvanschie.inventoryframework + 0.10.18 + ../../pom.xml + + 4.0.0 + + 1_18_2 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.18.2-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + + \ No newline at end of file diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/AnvilInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/AnvilInventoryImpl.java new file mode 100644 index 000000000..c12daf435 --- /dev/null +++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/AnvilInventoryImpl.java @@ -0,0 +1,283 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_2; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.18.2 + * + * @since 0.10.5 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.5 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.5 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.5 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.5 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.5 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public void setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/BeaconInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/BeaconInventoryImpl.java new file mode 100644 index 000000000..094ed0e72 --- /dev/null +++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/BeaconInventoryImpl.java @@ -0,0 +1,199 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_2; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.18.2 + * + * @since 0.10.5 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + TranslatableComponent message = new TranslatableComponent("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, message)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.5 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.5 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/CartographyTableInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..4075fa17f --- /dev/null +++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/CartographyTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_2; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.18.2 + * + * @since 0.10.5 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.5 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.5 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/EnchantingTableInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..862287315 --- /dev/null +++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/EnchantingTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_2; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.18.2 + * + * @since 0.10.5 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.5 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.5 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/GrindstoneInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..1dbb1be6a --- /dev/null +++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/GrindstoneInventoryImpl.java @@ -0,0 +1,205 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_2; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.18.2 + * + * @since 0.10.5 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.17.1"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.5 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.5 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.5 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/MerchantInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/MerchantInventoryImpl.java new file mode 100644 index 000000000..c1a9a6ede --- /dev/null +++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_2; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.18.2 + * + * @since 0.10.5 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.5 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/SmithingTableInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..d2776935f --- /dev/null +++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/SmithingTableInventoryImpl.java @@ -0,0 +1,255 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_2; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.18.2 + * + * @since 0.10.5 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerSmithingTable; + + int id = containerSmithingTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.SMITHING, message)); + + sendItems(player, items, player.getItemOnCursor()); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.5 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.5 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.5 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.5 + */ + private class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.player = serverPlayer.getBukkitEntity(); + + inputSlots.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inputSlots.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultSlots.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(access.getLocation(), inputSlots, resultSlots) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + } +} diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/StonecutterInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/StonecutterInventoryImpl.java new file mode 100644 index 000000000..b9598e81d --- /dev/null +++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/StonecutterInventoryImpl.java @@ -0,0 +1,219 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_2; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.18.2 + * + * @since 0.10.5 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.5 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.5 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/CustomInventoryUtil.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/CustomInventoryUtil.java new file mode 100644 index 000000000..1f20cc3ca --- /dev/null +++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_2.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.5 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/TextHolderUtil.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/TextHolderUtil.java new file mode 100644 index 000000000..2e2fe9542 --- /dev/null +++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/TextHolderUtil.java @@ -0,0 +1,66 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_18_2.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.5 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return new TextComponent(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.5 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_19_0/pom.xml b/nms/1_19_0/pom.xml new file mode 100644 index 000000000..db868d974 --- /dev/null +++ b/nms/1_19_0/pom.xml @@ -0,0 +1,58 @@ + + + + IF-parent + com.github.stefvanschie.inventoryframework + 0.10.18 + ../../pom.xml + + 4.0.0 + + 1_19_0 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.19-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + \ No newline at end of file diff --git a/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/AnvilInventoryImpl.java b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/AnvilInventoryImpl.java new file mode 100644 index 000000000..dc578277a --- /dev/null +++ b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/AnvilInventoryImpl.java @@ -0,0 +1,283 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_0; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_0.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.19 + * + * @since 0.10.6 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.6 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.6 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.6 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.6 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.6 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public void setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/BeaconInventoryImpl.java b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/BeaconInventoryImpl.java new file mode 100644 index 000000000..4ea1651d7 --- /dev/null +++ b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/BeaconInventoryImpl.java @@ -0,0 +1,199 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_0; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.19 + * + * @since 0.10.6 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.6 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.6 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/CartographyTableInventoryImpl.java b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..222f3f661 --- /dev/null +++ b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/CartographyTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_0; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.19 + * + * @since 0.10.6 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.6 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.6 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/EnchantingTableInventoryImpl.java b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..c560bc371 --- /dev/null +++ b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/EnchantingTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_0; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.19 + * + * @since 0.10.6 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.6 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.6 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/GrindstoneInventoryImpl.java b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..6795a9358 --- /dev/null +++ b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/GrindstoneInventoryImpl.java @@ -0,0 +1,205 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_0; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.19 + * + * @since 0.10.6 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.17.1"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.6 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.6 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.6 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/MerchantInventoryImpl.java b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/MerchantInventoryImpl.java new file mode 100644 index 000000000..1be13bd16 --- /dev/null +++ b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_0; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.19 + * + * @since 0.10.6 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.6 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/SmithingTableInventoryImpl.java b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..f393093a2 --- /dev/null +++ b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/SmithingTableInventoryImpl.java @@ -0,0 +1,255 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_0; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_0.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.19 + * + * @since 0.10.6 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerSmithingTable; + + int id = containerSmithingTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.SMITHING, message)); + + sendItems(player, items, player.getItemOnCursor()); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.6 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.6 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.6 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.6 + */ + private class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.player = serverPlayer.getBukkitEntity(); + + inputSlots.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inputSlots.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultSlots.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(access.getLocation(), inputSlots, resultSlots) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + } +} diff --git a/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/StonecutterInventoryImpl.java b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/StonecutterInventoryImpl.java new file mode 100644 index 000000000..ceaaefd48 --- /dev/null +++ b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/StonecutterInventoryImpl.java @@ -0,0 +1,219 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_0; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.19 + * + * @since 0.10.6 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.6 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.6 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/util/CustomInventoryUtil.java b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/util/CustomInventoryUtil.java new file mode 100644 index 000000000..2b26b05fe --- /dev/null +++ b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_0.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.6 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/util/TextHolderUtil.java b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/util/TextHolderUtil.java new file mode 100644 index 000000000..470eecbea --- /dev/null +++ b/nms/1_19_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_0/util/TextHolderUtil.java @@ -0,0 +1,65 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_0.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.6 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.6 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_19_1/pom.xml b/nms/1_19_1/pom.xml new file mode 100644 index 000000000..2510e437f --- /dev/null +++ b/nms/1_19_1/pom.xml @@ -0,0 +1,58 @@ + + + + IF-parent + com.github.stefvanschie.inventoryframework + 0.10.18 + ../../pom.xml + + 4.0.0 + + 1_19_1 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.19.1-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + \ No newline at end of file diff --git a/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/AnvilInventoryImpl.java b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/AnvilInventoryImpl.java new file mode 100644 index 000000000..aefaaa939 --- /dev/null +++ b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/AnvilInventoryImpl.java @@ -0,0 +1,283 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_1; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_1.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.19.1 + * + * @since 0.10.7 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.7 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.7 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.7 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.7 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public void setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/BeaconInventoryImpl.java b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/BeaconInventoryImpl.java new file mode 100644 index 000000000..0f6c40c68 --- /dev/null +++ b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/BeaconInventoryImpl.java @@ -0,0 +1,199 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_1; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.19.1 + * + * @since 0.10.7 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.7 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/CartographyTableInventoryImpl.java b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..3e16d2013 --- /dev/null +++ b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/CartographyTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_1; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.19.1 + * + * @since 0.10.7 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.7 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/EnchantingTableInventoryImpl.java b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..79f19e57f --- /dev/null +++ b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/EnchantingTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_1; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.19.1 + * + * @since 0.10.7 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.7 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/GrindstoneInventoryImpl.java b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..b89208af6 --- /dev/null +++ b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/GrindstoneInventoryImpl.java @@ -0,0 +1,205 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_1; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.19.1 + * + * @since 0.10.7 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.1"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.7 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.7 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/MerchantInventoryImpl.java b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/MerchantInventoryImpl.java new file mode 100644 index 000000000..baf2108f7 --- /dev/null +++ b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_1; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.19.1 + * + * @since 0.10.7 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/SmithingTableInventoryImpl.java b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..1c346d14c --- /dev/null +++ b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/SmithingTableInventoryImpl.java @@ -0,0 +1,255 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_1; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_1.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.19.1 + * + * @since 0.10.7 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerSmithingTable; + + int id = containerSmithingTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.SMITHING, message)); + + sendItems(player, items, player.getItemOnCursor()); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.7 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.7 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.7 + */ + private class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.player = serverPlayer.getBukkitEntity(); + + inputSlots.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inputSlots.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultSlots.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(access.getLocation(), inputSlots, resultSlots) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + } +} diff --git a/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/StonecutterInventoryImpl.java b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/StonecutterInventoryImpl.java new file mode 100644 index 000000000..cb634ed0e --- /dev/null +++ b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/StonecutterInventoryImpl.java @@ -0,0 +1,219 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_1; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.19.1 + * + * @since 0.10.7 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.7 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/util/CustomInventoryUtil.java b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/util/CustomInventoryUtil.java new file mode 100644 index 000000000..3c826da20 --- /dev/null +++ b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_1.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.7 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/util/TextHolderUtil.java b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/util/TextHolderUtil.java new file mode 100644 index 000000000..058d4ad06 --- /dev/null +++ b/nms/1_19_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_1/util/TextHolderUtil.java @@ -0,0 +1,65 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_1.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.7 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_19_2/pom.xml b/nms/1_19_2/pom.xml new file mode 100644 index 000000000..a9d1888f9 --- /dev/null +++ b/nms/1_19_2/pom.xml @@ -0,0 +1,58 @@ + + + + IF-parent + com.github.stefvanschie.inventoryframework + 0.10.18 + ../../pom.xml + + 4.0.0 + + 1_19_2 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.19.2-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + \ No newline at end of file diff --git a/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/AnvilInventoryImpl.java b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/AnvilInventoryImpl.java new file mode 100644 index 000000000..e06ee5772 --- /dev/null +++ b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/AnvilInventoryImpl.java @@ -0,0 +1,283 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_2; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_2.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.19.2 + * + * @since 0.10.7 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.7 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.7 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.7 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.7 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public void setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/BeaconInventoryImpl.java b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/BeaconInventoryImpl.java new file mode 100644 index 000000000..5ae4ba937 --- /dev/null +++ b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/BeaconInventoryImpl.java @@ -0,0 +1,199 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_2; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.19.2 + * + * @since 0.10.7 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.7 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/CartographyTableInventoryImpl.java b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..bf6b8303c --- /dev/null +++ b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/CartographyTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_2; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.19.2 + * + * @since 0.10.7 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.7 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/EnchantingTableInventoryImpl.java b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..f519e751e --- /dev/null +++ b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/EnchantingTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_2; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.19.2 + * + * @since 0.10.7 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.7 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/GrindstoneInventoryImpl.java b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..bcf8715e4 --- /dev/null +++ b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/GrindstoneInventoryImpl.java @@ -0,0 +1,205 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_2; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.19.2 + * + * @since 0.10.7 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.7 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.7 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/MerchantInventoryImpl.java b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/MerchantInventoryImpl.java new file mode 100644 index 000000000..1909a5147 --- /dev/null +++ b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_2; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.19.2 + * + * @since 0.10.7 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/SmithingTableInventoryImpl.java b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..3f30ef670 --- /dev/null +++ b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/SmithingTableInventoryImpl.java @@ -0,0 +1,255 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_2; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_2.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.19.2 + * + * @since 0.10.7 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerSmithingTable; + + int id = containerSmithingTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.SMITHING, message)); + + sendItems(player, items, player.getItemOnCursor()); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.7 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.7 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.7 + */ + private class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.player = serverPlayer.getBukkitEntity(); + + inputSlots.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inputSlots.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultSlots.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(access.getLocation(), inputSlots, resultSlots) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + } +} diff --git a/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/StonecutterInventoryImpl.java b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/StonecutterInventoryImpl.java new file mode 100644 index 000000000..a6c3f38fd --- /dev/null +++ b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/StonecutterInventoryImpl.java @@ -0,0 +1,219 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_2; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.19.2 + * + * @since 0.10.7 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.7 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.7 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/util/CustomInventoryUtil.java b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/util/CustomInventoryUtil.java new file mode 100644 index 000000000..fcda8d03c --- /dev/null +++ b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_2.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.7 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/util/TextHolderUtil.java b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/util/TextHolderUtil.java new file mode 100644 index 000000000..5760f79be --- /dev/null +++ b/nms/1_19_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_2/util/TextHolderUtil.java @@ -0,0 +1,65 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_2.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.7 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.7 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_19_3/pom.xml b/nms/1_19_3/pom.xml new file mode 100644 index 000000000..1f7924792 --- /dev/null +++ b/nms/1_19_3/pom.xml @@ -0,0 +1,58 @@ + + + + IF-parent + com.github.stefvanschie.inventoryframework + 0.10.18 + ../../pom.xml + + 4.0.0 + + 1_19_3 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.19.3-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + \ No newline at end of file diff --git a/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/AnvilInventoryImpl.java b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/AnvilInventoryImpl.java new file mode 100644 index 000000000..62af7ade7 --- /dev/null +++ b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/AnvilInventoryImpl.java @@ -0,0 +1,283 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_3; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_3.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.19.3 + * + * @since 0.10.8 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.8 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.8 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.8 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.8 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.8 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.8 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public void setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/BeaconInventoryImpl.java b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/BeaconInventoryImpl.java new file mode 100644 index 000000000..52488aab0 --- /dev/null +++ b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/BeaconInventoryImpl.java @@ -0,0 +1,199 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_3; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.19.3 + * + * @since 0.10.8 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.8 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.8 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/CartographyTableInventoryImpl.java b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..d0edc3de7 --- /dev/null +++ b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/CartographyTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_3; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.19.3 + * + * @since 0.10.8 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.8 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.8 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/EnchantingTableInventoryImpl.java b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..667957135 --- /dev/null +++ b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/EnchantingTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_3; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.19.3 + * + * @since 0.10.8 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.8 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.8 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/GrindstoneInventoryImpl.java b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..f1a75f995 --- /dev/null +++ b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/GrindstoneInventoryImpl.java @@ -0,0 +1,205 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_3; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.19.3 + * + * @since 0.10.8 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.8 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.8 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.8 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @since 0.10.8 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/MerchantInventoryImpl.java b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/MerchantInventoryImpl.java new file mode 100644 index 000000000..7dcb9dda0 --- /dev/null +++ b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_3; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.19.3 + * + * @since 0.10.8 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.8 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/SmithingTableInventoryImpl.java b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..da99add0b --- /dev/null +++ b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/SmithingTableInventoryImpl.java @@ -0,0 +1,255 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_3; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_3.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.19.3 + * + * @since 0.10.8 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerSmithingTable; + + int id = containerSmithingTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.SMITHING, message)); + + sendItems(player, items, player.getItemOnCursor()); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.8 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.8 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.8 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.8 + */ + private class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.player = serverPlayer.getBukkitEntity(); + + inputSlots.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inputSlots.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultSlots.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(access.getLocation(), inputSlots, resultSlots) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + } +} diff --git a/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/StonecutterInventoryImpl.java b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/StonecutterInventoryImpl.java new file mode 100644 index 000000000..9a124704e --- /dev/null +++ b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/StonecutterInventoryImpl.java @@ -0,0 +1,219 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_3; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.19.3 + * + * @since 0.10.8 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.8 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.8 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/util/CustomInventoryUtil.java b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/util/CustomInventoryUtil.java new file mode 100644 index 000000000..7cd7b7f91 --- /dev/null +++ b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_3.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.8 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/util/TextHolderUtil.java b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/util/TextHolderUtil.java new file mode 100644 index 000000000..6508159e0 --- /dev/null +++ b/nms/1_19_3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_3/util/TextHolderUtil.java @@ -0,0 +1,65 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_3.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.8 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.8 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_19_4/pom.xml b/nms/1_19_4/pom.xml new file mode 100644 index 000000000..9d84bb3b0 --- /dev/null +++ b/nms/1_19_4/pom.xml @@ -0,0 +1,58 @@ + + + + IF-parent + com.github.stefvanschie.inventoryframework + 0.10.18 + ../../pom.xml + + 4.0.0 + + 1_19_4 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.19.4-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + \ No newline at end of file diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/AnvilInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/AnvilInventoryImpl.java new file mode 100644 index 000000000..877668403 --- /dev/null +++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/AnvilInventoryImpl.java @@ -0,0 +1,283 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_4; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.19.4 + * + * @since 0.10.9 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.9 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.9 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.9 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10. + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.9 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.9 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public void setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/BeaconInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/BeaconInventoryImpl.java new file mode 100644 index 000000000..c3359e7f0 --- /dev/null +++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/BeaconInventoryImpl.java @@ -0,0 +1,199 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_4; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.19.4 + * + * @since 0.10.9 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.9 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.9 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/CartographyTableInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..3cbef8b2d --- /dev/null +++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/CartographyTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_4; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.19.4 + * + * @since 0.10.9 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.9 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.9 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/EnchantingTableInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..bc95beaaf --- /dev/null +++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/EnchantingTableInventoryImpl.java @@ -0,0 +1,213 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_4; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.19.4 + * + * @since 0.10.9 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.9 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.9 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/GrindstoneInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..20288623b --- /dev/null +++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/GrindstoneInventoryImpl.java @@ -0,0 +1,205 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_4; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.19.4 + * + * @since 0.10.9 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.9 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.9 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.9 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @since 0.10.9 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/LegacySmithingTableInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/LegacySmithingTableInventoryImpl.java new file mode 100644 index 000000000..d4232dd49 --- /dev/null +++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/LegacySmithingTableInventoryImpl.java @@ -0,0 +1,257 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_4; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.LegacySmithingMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventorySmithing; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.19.4. This smithing table is for prior to Minecraft 1.20. + * + * @since 0.10.9 + * @deprecated this type of smithing table will be removed in Minecraft 1.20 + */ +@Deprecated +public class LegacySmithingTableInventoryImpl extends SmithingTableInventory { + + public LegacySmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Nullable + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a legacy smithing table should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerSmithingTable; + + int id = containerSmithingTable.containerId; + Component message = TextHolderUtil.toComponent(title); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.LEGACY_SMITHING, message)); + + sendItems(player, items, player.getItemOnCursor()); + + return null; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.9 + */ + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.9 + */ + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.9 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.9 + */ + private class ContainerSmithingTableImpl extends LegacySmithingMenu { + + /** + * The player for this smithing table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container smithing table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.player = serverPlayer.getBukkitEntity(); + + inputSlots.setItem(0, CraftItemStack.asNMSCopy(items[0])); + inputSlots.setItem(1, CraftItemStack.asNMSCopy(items[1])); + resultSlots.setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventorySmithing(access.getLocation(), inputSlots, resultSlots) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + } +} diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/MerchantInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/MerchantInventoryImpl.java new file mode 100644 index 000000000..b1a0b13ab --- /dev/null +++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_4; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.19.4 + * + * @since 0.10.9 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.9 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/SmithingTableInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..a3dc68a37 --- /dev/null +++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/SmithingTableInventoryImpl.java @@ -0,0 +1,335 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_4; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.19.4. This smithing table is for Minecraft 1.19.4 or higher. + * + * @since 0.10.9 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @NotNull + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 4) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 4, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, message); + + Inventory inventory = containerSmithingTable.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + inventory.setItem(3, items[3]); + + int containerId = containerSmithingTable.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.SMITHING, message)); + serverPlayer.containerMenu = containerSmithingTable; + serverPlayer.initMenu(containerSmithingTable); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.9 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.9 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.9 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.9 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.9 + */ + private static class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * Creates a new custom smithing table container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.9 + */ + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + setTitle(title); + + this.checkReachable = false; + + Slot slotOne = this.slots.get(0); + Slot slotTwo = this.slots.get(1); + Slot slotThree = this.slots.get(2); + Slot slotFour = this.slots.get(3); + + this.slots.set(0, new Slot(slotOne.container, slotOne.index, slotOne.x, slotOne.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotOne.onTake(player, stack); + } + }); + + this.slots.set(1, new Slot(slotTwo.container, slotTwo.index, slotTwo.x, slotTwo.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotTwo.onTake(player, stack); + } + }); + + this.slots.set(2, new Slot(slotThree.container, slotThree.index, slotThree.x, slotThree.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotThree.onTake(player, stack); + } + }); + + this.slots.set(3, new Slot(slotFour.container, slotFour.index, slotFour.x, slotFour.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotFour.onTake(player, stack); + } + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @Override + public void createResult() {} + + @Override + protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + + @Override + protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) { + return true; + } + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/StonecutterInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/StonecutterInventoryImpl.java new file mode 100644 index 000000000..ba23f087b --- /dev/null +++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/StonecutterInventoryImpl.java @@ -0,0 +1,219 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_4; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.19.4 + * + * @since 0.10.9 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + Component message = TextHolderUtil.toComponent(title); + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.9 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.9 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/CustomInventoryUtil.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/CustomInventoryUtil.java new file mode 100644 index 000000000..317736273 --- /dev/null +++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_4.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10. + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/TextHolderUtil.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/TextHolderUtil.java new file mode 100644 index 000000000..bf999b34d --- /dev/null +++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/TextHolderUtil.java @@ -0,0 +1,65 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_19_4.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.9 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.9 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_20_0/pom.xml b/nms/1_20_0/pom.xml new file mode 100644 index 000000000..4197a5b5f --- /dev/null +++ b/nms/1_20_0/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + 1_20_0 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.20-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + \ No newline at end of file diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/AnvilInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/AnvilInventoryImpl.java new file mode 100644 index 000000000..1507fe2ed --- /dev/null +++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/AnvilInventoryImpl.java @@ -0,0 +1,284 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_0; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.20.0 + * + * @since 0.10.14 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.14 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.14 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public boolean setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + return true; //no idea what this is for + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/BeaconInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/BeaconInventoryImpl.java new file mode 100644 index 000000000..da29ddbfd --- /dev/null +++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/BeaconInventoryImpl.java @@ -0,0 +1,200 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_0; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.20.0 + * + * @since 0.10.14 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.14 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + setTitle(Component.empty()); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/CartographyTableInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..6cc1af680 --- /dev/null +++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/CartographyTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_0; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.20.0 + * + * @since 0.10.14 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.14 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/EnchantingTableInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..0bb826224 --- /dev/null +++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/EnchantingTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_0; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.20.0 + * + * @since 0.10.14 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.14 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/GrindstoneInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..842fcc910 --- /dev/null +++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/GrindstoneInventoryImpl.java @@ -0,0 +1,208 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_0; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.20.0 + * + * @since 0.10.14 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer, message); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.14 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @param title the title of the gui + * @since 0.10.14 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + setTitle(title); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/MerchantInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/MerchantInventoryImpl.java new file mode 100644 index 000000000..9eb421d0e --- /dev/null +++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_0; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.20.0 + * + * @since 0.10.14 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/SmithingTableInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..82fd2df81 --- /dev/null +++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/SmithingTableInventoryImpl.java @@ -0,0 +1,335 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_0; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.20.0. This is only available for Minecraft 1.20 and higher. + * + * @since 0.10.14 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @NotNull + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 4) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 4, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, message); + + Inventory inventory = containerSmithingTable.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + inventory.setItem(3, items[3]); + + int containerId = containerSmithingTable.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.SMITHING, message)); + serverPlayer.containerMenu = containerSmithingTable; + serverPlayer.initMenu(containerSmithingTable); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.14 + */ + private static class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * Creates a new custom smithing table container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.14 + */ + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + setTitle(title); + + this.checkReachable = false; + + Slot slotOne = this.slots.get(0); + Slot slotTwo = this.slots.get(1); + Slot slotThree = this.slots.get(2); + Slot slotFour = this.slots.get(3); + + this.slots.set(0, new Slot(slotOne.container, slotOne.index, slotOne.x, slotOne.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotOne.onTake(player, stack); + } + }); + + this.slots.set(1, new Slot(slotTwo.container, slotTwo.index, slotTwo.x, slotTwo.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotTwo.onTake(player, stack); + } + }); + + this.slots.set(2, new Slot(slotThree.container, slotThree.index, slotThree.x, slotThree.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotThree.onTake(player, stack); + } + }); + + this.slots.set(3, new Slot(slotFour.container, slotFour.index, slotFour.x, slotFour.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotFour.onTake(player, stack); + } + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @Override + public void createResult() {} + + @Override + protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + + @Override + protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) { + return true; + } + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/StonecutterInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/StonecutterInventoryImpl.java new file mode 100644 index 000000000..6bfdb19d8 --- /dev/null +++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/StonecutterInventoryImpl.java @@ -0,0 +1,221 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_0; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.20.0 + * + * @since 0.10.14 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items, message); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.14 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, @NotNull Component title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/CustomInventoryUtil.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/CustomInventoryUtil.java new file mode 100644 index 000000000..5050a7885 --- /dev/null +++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_0.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.14 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/TextHolderUtil.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/TextHolderUtil.java new file mode 100644 index 000000000..ef64afcc5 --- /dev/null +++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/TextHolderUtil.java @@ -0,0 +1,65 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_0.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.14 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_20_1/pom.xml b/nms/1_20_1/pom.xml new file mode 100644 index 000000000..d6231fee6 --- /dev/null +++ b/nms/1_20_1/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + 1_20_1 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.20.1-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + \ No newline at end of file diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/AnvilInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/AnvilInventoryImpl.java new file mode 100644 index 000000000..ec4284832 --- /dev/null +++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/AnvilInventoryImpl.java @@ -0,0 +1,284 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_1; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.20.1 + * + * @since 0.10.14 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.14 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.14 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public boolean setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + return true; //no idea what this is for + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/BeaconInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/BeaconInventoryImpl.java new file mode 100644 index 000000000..ecbae2c22 --- /dev/null +++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/BeaconInventoryImpl.java @@ -0,0 +1,200 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_1; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.20.1 + * + * @since 0.10.14 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.14 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + setTitle(Component.empty()); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("r"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/CartographyTableInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..6d3f7af35 --- /dev/null +++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/CartographyTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_1; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.20.1 + * + * @since 0.10.14 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.14 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/EnchantingTableInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..c16480009 --- /dev/null +++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/EnchantingTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_1; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.20.1 + * + * @since 0.10.14 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.14 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("n"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/GrindstoneInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..35bd3c5d1 --- /dev/null +++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/GrindstoneInventoryImpl.java @@ -0,0 +1,208 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_1; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.20.1 + * + * @since 0.10.14 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer, message); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.14 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @param title the title of the gui + * @since 0.10.14 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + setTitle(title); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/MerchantInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/MerchantInventoryImpl.java new file mode 100644 index 000000000..687afca70 --- /dev/null +++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_1; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.20.1 + * + * @since 0.10.14 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/SmithingTableInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..30be2e530 --- /dev/null +++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/SmithingTableInventoryImpl.java @@ -0,0 +1,335 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_1; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.20.1. This is only available for Minecraft 1.20 and higher. + * + * @since 0.10.14 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @NotNull + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 4) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 4, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, message); + + Inventory inventory = containerSmithingTable.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + inventory.setItem(3, items[3]); + + int containerId = containerSmithingTable.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.SMITHING, message)); + serverPlayer.containerMenu = containerSmithingTable; + serverPlayer.initMenu(containerSmithingTable); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.14 + */ + private static class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * Creates a new custom smithing table container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.14 + */ + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + setTitle(title); + + this.checkReachable = false; + + Slot slotOne = this.slots.get(0); + Slot slotTwo = this.slots.get(1); + Slot slotThree = this.slots.get(2); + Slot slotFour = this.slots.get(3); + + this.slots.set(0, new Slot(slotOne.container, slotOne.index, slotOne.x, slotOne.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotOne.onTake(player, stack); + } + }); + + this.slots.set(1, new Slot(slotTwo.container, slotTwo.index, slotTwo.x, slotTwo.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotTwo.onTake(player, stack); + } + }); + + this.slots.set(2, new Slot(slotThree.container, slotThree.index, slotThree.x, slotThree.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotThree.onTake(player, stack); + } + }); + + this.slots.set(3, new Slot(slotFour.container, slotFour.index, slotFour.x, slotFour.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotFour.onTake(player, stack); + } + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @Override + public void createResult() {} + + @Override + protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + + @Override + protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) { + return true; + } + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/StonecutterInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/StonecutterInventoryImpl.java new file mode 100644 index 000000000..53d96f9c5 --- /dev/null +++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/StonecutterInventoryImpl.java @@ -0,0 +1,221 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_1; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.20.1 + * + * @since 0.10.14 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items, message); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.14 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, @NotNull Component title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/CustomInventoryUtil.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/CustomInventoryUtil.java new file mode 100644 index 000000000..ed08ff8f4 --- /dev/null +++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_1.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.14 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/TextHolderUtil.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/TextHolderUtil.java new file mode 100644 index 000000000..3438bb279 --- /dev/null +++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/TextHolderUtil.java @@ -0,0 +1,65 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_1.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.14 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_20_2/pom.xml b/nms/1_20_2/pom.xml new file mode 100644 index 000000000..7e531ea41 --- /dev/null +++ b/nms/1_20_2/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + 1_20_2 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.20.2-SNAPSHOT + provided + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + + \ No newline at end of file diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/AnvilInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/AnvilInventoryImpl.java new file mode 100644 index 000000000..75581419a --- /dev/null +++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/AnvilInventoryImpl.java @@ -0,0 +1,284 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_2; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.20.2 + * + * @since 0.10.12 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.12 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.12 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.12 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.12 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.12 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.12 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public boolean setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + return true; //no idea what this is for + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/BeaconInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/BeaconInventoryImpl.java new file mode 100644 index 000000000..601e1f310 --- /dev/null +++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/BeaconInventoryImpl.java @@ -0,0 +1,200 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_2; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.20.2 + * + * @since 0.10.12 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.12 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.12 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + setTitle(Component.empty()); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("s"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/CartographyTableInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..a8362db7b --- /dev/null +++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/CartographyTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_2; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.20.2 + * + * @since 0.10.12 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.12 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.12 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/EnchantingTableInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..2191c7b2e --- /dev/null +++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/EnchantingTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_2; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.20.2 + * + * @since 0.10.12 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.12 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.12 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("o"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/GrindstoneInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..c915baccb --- /dev/null +++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/GrindstoneInventoryImpl.java @@ -0,0 +1,208 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_2; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.20.2 + * + * @since 0.10.12 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer, message); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.12 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.12 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.12 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @param title the title of the gui + * @since 0.10.12 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + setTitle(title); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/MerchantInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/MerchantInventoryImpl.java new file mode 100644 index 000000000..5302ffaff --- /dev/null +++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_2; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.20.2 + * + * @since 0.10.12 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.12 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/SmithingTableInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..73e6ce304 --- /dev/null +++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/SmithingTableInventoryImpl.java @@ -0,0 +1,335 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_2; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.20.2. This is only available for Minecraft 1.20 and higher. + * + * @since 0.10.12 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @NotNull + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 4) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 4, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, message); + + Inventory inventory = containerSmithingTable.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + inventory.setItem(3, items[3]); + + int containerId = containerSmithingTable.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.SMITHING, message)); + serverPlayer.containerMenu = containerSmithingTable; + serverPlayer.initMenu(containerSmithingTable); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.12 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.12 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.12 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.12 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.12 + */ + private static class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * Creates a new custom smithing table container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.12 + */ + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + setTitle(title); + + this.checkReachable = false; + + Slot slotOne = this.slots.get(0); + Slot slotTwo = this.slots.get(1); + Slot slotThree = this.slots.get(2); + Slot slotFour = this.slots.get(3); + + this.slots.set(0, new Slot(slotOne.container, slotOne.index, slotOne.x, slotOne.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotOne.onTake(player, stack); + } + }); + + this.slots.set(1, new Slot(slotTwo.container, slotTwo.index, slotTwo.x, slotTwo.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotTwo.onTake(player, stack); + } + }); + + this.slots.set(2, new Slot(slotThree.container, slotThree.index, slotThree.x, slotThree.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotThree.onTake(player, stack); + } + }); + + this.slots.set(3, new Slot(slotFour.container, slotFour.index, slotFour.x, slotFour.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotFour.onTake(player, stack); + } + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @Override + public void createResult() {} + + @Override + protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + + @Override + protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) { + return true; + } + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/StonecutterInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/StonecutterInventoryImpl.java new file mode 100644 index 000000000..46f13264b --- /dev/null +++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/StonecutterInventoryImpl.java @@ -0,0 +1,221 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_2; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.20.2 + * + * @since 0.10.12 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items, message); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.12 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.12 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, @NotNull Component title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/CustomInventoryUtil.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/CustomInventoryUtil.java new file mode 100644 index 000000000..66f18dbff --- /dev/null +++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_2.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.12 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/TextHolderUtil.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/TextHolderUtil.java new file mode 100644 index 000000000..d1bc6fe98 --- /dev/null +++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/TextHolderUtil.java @@ -0,0 +1,65 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_2.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.12 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.12 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_20_3-4/pom.xml b/nms/1_20_3-4/pom.xml new file mode 100644 index 000000000..6296cc761 --- /dev/null +++ b/nms/1_20_3-4/pom.xml @@ -0,0 +1,110 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + 1_20_3-4 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + ca.bkaw + paper-nms + 1.20.3-SNAPSHOT + provided + + + + + + + + net.kyori + adventure-api + 4.17.0 + + + net.kyori + adventure-key + 4.17.0 + + + net.kyori + adventure-text-logger-slf4j + 4.17.0 + + + net.kyori + adventure-text-minimessage + 4.17.0 + + + net.kyori + adventure-text-serializer-ansi + 4.17.0 + + + net.kyori + adventure-text-serializer-gson + 4.15.0 + + + net.kyori + adventure-text-serializer-json + 4.15.0 + + + net.kyori + adventure-text-serializer-legacy + 4.17.0 + + + net.kyori + adventure-text-serializer-plain + 4.15.0 + + + + + + + + ca.bkaw + paper-nms-maven-plugin + 1.4.4 + + + process-classes + + remap + + + + + + + + + + bytecode.space + https://repo.bytecode.space/repository/maven-public/ + + + + \ No newline at end of file diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/AnvilInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/AnvilInventoryImpl.java new file mode 100644 index 000000000..9f5691dce --- /dev/null +++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/AnvilInventoryImpl.java @@ -0,0 +1,284 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_3; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.20.3 + * + * @since 0.10.13 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.13 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.13 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.13 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.13 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.13 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.13 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public boolean setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + return true; //no idea what this is for + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/BeaconInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/BeaconInventoryImpl.java new file mode 100644 index 000000000..154a99f41 --- /dev/null +++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/BeaconInventoryImpl.java @@ -0,0 +1,200 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_3; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.20.3 + * + * @since 0.10.13 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.13 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.13 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + setTitle(Component.empty()); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("s"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/CartographyTableInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..db1f66cca --- /dev/null +++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/CartographyTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_3; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.20.3 + * + * @since 0.10.13 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.13 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.13 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/EnchantingTableInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..7f536a9de --- /dev/null +++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/EnchantingTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_3; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.20.3 + * + * @since 0.10.13 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.13 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.13 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("o"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/GrindstoneInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..f109eb023 --- /dev/null +++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/GrindstoneInventoryImpl.java @@ -0,0 +1,208 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_3; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.20.3 + * + * @since 0.10.13 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer, message); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.13 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.13 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.13 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @param title the title of the gui + * @since 0.10.13 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + setTitle(title); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/MerchantInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/MerchantInventoryImpl.java new file mode 100644 index 000000000..da6be654d --- /dev/null +++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/MerchantInventoryImpl.java @@ -0,0 +1,97 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_3; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * Internal merchant inventory for 1.20.3 + * + * @since 0.10.13 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.13 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/SmithingTableInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..3513d80a3 --- /dev/null +++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/SmithingTableInventoryImpl.java @@ -0,0 +1,335 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_3; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.20.3. This is only available for Minecraft 1.20 and higher. + * + * @since 0.10.13 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @NotNull + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 4) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 4, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, message); + + Inventory inventory = containerSmithingTable.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + inventory.setItem(3, items[3]); + + int containerId = containerSmithingTable.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.SMITHING, message)); + serverPlayer.containerMenu = containerSmithingTable; + serverPlayer.initMenu(containerSmithingTable); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.13 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.13 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.13 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.13 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.13 + */ + private static class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * Creates a new custom smithing table container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.13 + */ + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + setTitle(title); + + this.checkReachable = false; + + Slot slotOne = this.slots.get(0); + Slot slotTwo = this.slots.get(1); + Slot slotThree = this.slots.get(2); + Slot slotFour = this.slots.get(3); + + this.slots.set(0, new Slot(slotOne.container, slotOne.index, slotOne.x, slotOne.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotOne.onTake(player, stack); + } + }); + + this.slots.set(1, new Slot(slotTwo.container, slotTwo.index, slotTwo.x, slotTwo.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotTwo.onTake(player, stack); + } + }); + + this.slots.set(2, new Slot(slotThree.container, slotThree.index, slotThree.x, slotThree.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotThree.onTake(player, stack); + } + }); + + this.slots.set(3, new Slot(slotFour.container, slotFour.index, slotFour.x, slotFour.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotFour.onTake(player, stack); + } + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @Override + public void createResult() {} + + @Override + protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + + @Override + protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) { + return true; + } + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/StonecutterInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/StonecutterInventoryImpl.java new file mode 100644 index 000000000..eef0dc207 --- /dev/null +++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/StonecutterInventoryImpl.java @@ -0,0 +1,221 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_3; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.20.3 + * + * @since 0.10.13 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items, message); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.13 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.13 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, @NotNull Component title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/CustomInventoryUtil.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/CustomInventoryUtil.java new file mode 100644 index 000000000..3a6cdb8d1 --- /dev/null +++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_3.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.13 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/TextHolderUtil.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/TextHolderUtil.java new file mode 100644 index 000000000..f5cbc26ee --- /dev/null +++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/TextHolderUtil.java @@ -0,0 +1,65 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_3.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.13 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.13 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson())); + } +} diff --git a/nms/1_20_5/pom.xml b/nms/1_20_5/pom.xml new file mode 100644 index 000000000..ed3a0704f --- /dev/null +++ b/nms/1_20_5/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + 1_20_5 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + org.spigotmc + spigot + 1.20.5-R0.1-SNAPSHOT + remapped-mojang + provided + + + + + + + net.md-5 + specialsource-maven-plugin + 2.0.3 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:1.20.5-R0.1-SNAPSHOT:txt:maps-mojang + true + org.spigotmc:spigot:1.20.5-R0.1-SNAPSHOT:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:1.20.5-R0.1-SNAPSHOT:csrg:maps-spigot + org.spigotmc:spigot:1.20.5-R0.1-SNAPSHOT:jar:remapped-obf + + + + + + + + \ No newline at end of file diff --git a/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/AnvilInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/AnvilInventoryImpl.java new file mode 100644 index 000000000..ac1a3ceec --- /dev/null +++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/AnvilInventoryImpl.java @@ -0,0 +1,284 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_5; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_5.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_5.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.20.5 + * + * @since 0.10.14 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.14 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.14 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public boolean setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + return true; //no idea what this is for + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/BeaconInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/BeaconInventoryImpl.java new file mode 100644 index 000000000..b5c91dfd9 --- /dev/null +++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/BeaconInventoryImpl.java @@ -0,0 +1,200 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_5; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.20.5 + * + * @since 0.10.14 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.14 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + setTitle(Component.empty()); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("s"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/CartographyTableInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..e7c4d926d --- /dev/null +++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/CartographyTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_5; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_5.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_5.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.20.5 + * + * @since 0.10.14 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.14 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/EnchantingTableInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..b6a9c4eba --- /dev/null +++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/EnchantingTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_5; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_5.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.20.5 + * + * @since 0.10.14 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.14 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("o"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/GrindstoneInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..b53b79b6f --- /dev/null +++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/GrindstoneInventoryImpl.java @@ -0,0 +1,208 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_5; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_5.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_5.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.20.5 + * + * @since 0.10.14 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer, message); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.14 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @param title the title of the gui + * @since 0.10.14 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + setTitle(title); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/MerchantInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/MerchantInventoryImpl.java new file mode 100644 index 000000000..e655a9e19 --- /dev/null +++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/MerchantInventoryImpl.java @@ -0,0 +1,119 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_5; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.core.component.DataComponentPredicate; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.trading.ItemCost; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Internal merchant inventory for 1.20.5 + * + * @since 0.10.14 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + ItemCost itemCostA = convertItemStackToItemCost(nmsItemA); + ItemCost itemCostB = convertItemStackToItemCost(nmsItemB); + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + itemCostA, Optional.of(itemCostB), nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Converts an NMS item stack to an item cost. + * + * @param itemStack the item stack to convert + * @return the item cost + * @since 0.10.14 + */ + @NotNull + @Contract(value = "_ -> new", pure = true) + private ItemCost convertItemStackToItemCost(@NotNull net.minecraft.world.item.ItemStack itemStack) { + DataComponentPredicate predicate = DataComponentPredicate.allOf(itemStack.getComponents()); + + return new ItemCost(itemStack.getItemHolder(), itemStack.getCount(), predicate, itemStack); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/SmithingTableInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..44710f594 --- /dev/null +++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/SmithingTableInventoryImpl.java @@ -0,0 +1,335 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_5; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_5.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_5.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.20.5. This is only available for Minecraft 1.20 and higher. + * + * @since 0.10.14 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @NotNull + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 4) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 4, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, message); + + Inventory inventory = containerSmithingTable.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + inventory.setItem(3, items[3]); + + int containerId = containerSmithingTable.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.SMITHING, message)); + serverPlayer.containerMenu = containerSmithingTable; + serverPlayer.initMenu(containerSmithingTable); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.14 + */ + private static class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * Creates a new custom smithing table container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.14 + */ + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + setTitle(title); + + this.checkReachable = false; + + Slot slotOne = this.slots.get(0); + Slot slotTwo = this.slots.get(1); + Slot slotThree = this.slots.get(2); + Slot slotFour = this.slots.get(3); + + this.slots.set(0, new Slot(slotOne.container, slotOne.index, slotOne.x, slotOne.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotOne.onTake(player, stack); + } + }); + + this.slots.set(1, new Slot(slotTwo.container, slotTwo.index, slotTwo.x, slotTwo.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotTwo.onTake(player, stack); + } + }); + + this.slots.set(2, new Slot(slotThree.container, slotThree.index, slotThree.x, slotThree.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotThree.onTake(player, stack); + } + }); + + this.slots.set(3, new Slot(slotFour.container, slotFour.index, slotFour.x, slotFour.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotFour.onTake(player, stack); + } + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @Override + public void createResult() {} + + @Override + protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + + @Override + protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) { + return true; + } + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/StonecutterInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/StonecutterInventoryImpl.java new file mode 100644 index 000000000..698b5fb36 --- /dev/null +++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/StonecutterInventoryImpl.java @@ -0,0 +1,221 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_5; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_5.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.20.5 + * + * @since 0.10.14 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items, message); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.14 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, @NotNull Component title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/CustomInventoryUtil.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/CustomInventoryUtil.java new file mode 100644 index 000000000..b735c1655 --- /dev/null +++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_5.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.14 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/TextHolderUtil.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/TextHolderUtil.java new file mode 100644 index 000000000..9f8bce785 --- /dev/null +++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/TextHolderUtil.java @@ -0,0 +1,67 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_5.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.core.HolderLookup; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.14 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson(), HolderLookup.Provider.create(Stream.empty()))); + } +} diff --git a/nms/1_20_6/pom.xml b/nms/1_20_6/pom.xml new file mode 100644 index 000000000..0a522e95d --- /dev/null +++ b/nms/1_20_6/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + 1_20_6 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + org.spigotmc + spigot + 1.20.6-R0.1-SNAPSHOT + remapped-mojang + provided + + + + + + + net.md-5 + specialsource-maven-plugin + 2.0.3 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:1.20.6-R0.1-SNAPSHOT:txt:maps-mojang + true + org.spigotmc:spigot:1.20.6-R0.1-SNAPSHOT:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:1.20.6-R0.1-SNAPSHOT:csrg:maps-spigot + org.spigotmc:spigot:1.20.6-R0.1-SNAPSHOT:jar:remapped-obf + + + + + + + + \ No newline at end of file diff --git a/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/AnvilInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/AnvilInventoryImpl.java new file mode 100644 index 000000000..65cfec658 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/AnvilInventoryImpl.java @@ -0,0 +1,284 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.20.6 + * + * @since 0.10.14 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.14 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.14 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public boolean setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + return true; //no idea what this is for + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/BeaconInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/BeaconInventoryImpl.java new file mode 100644 index 000000000..8d2b3914a --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/BeaconInventoryImpl.java @@ -0,0 +1,200 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.20.6 + * + * @since 0.10.14 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.14 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + setTitle(Component.empty()); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("s"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryBeacon((Container) beaconField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/CartographyTableInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..0e9624f28 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/CartographyTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.20.6 + * + * @since 0.10.14 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.14 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/EnchantingTableInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..d3a65c70c --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/EnchantingTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.20.6 + * + * @since 0.10.14 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.14 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("o"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + try { + CraftInventory inventory = new CraftInventoryEnchanting((Container) enchantSlotsField.get(this)) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/GrindstoneInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..04f79f892 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/GrindstoneInventoryImpl.java @@ -0,0 +1,208 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.20.6 + * + * @since 0.10.14 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer, message); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.14 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @param title the title of the gui + * @since 0.10.14 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + setTitle(title); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/MerchantInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/MerchantInventoryImpl.java new file mode 100644 index 000000000..4ec8a905b --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/MerchantInventoryImpl.java @@ -0,0 +1,118 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.core.component.DataComponentPredicate; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.ItemCost; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Internal merchant inventory for 1.20.6 + * + * @since 0.10.14 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + ItemCost itemCostA = convertItemStackToItemCost(nmsItemA); + ItemCost itemCostB = convertItemStackToItemCost(nmsItemB); + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + itemCostA, Optional.of(itemCostB), nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Converts an NMS item stack to an item cost. + * + * @param itemStack the item stack to convert + * @return the item cost + * @since 0.10.14 + */ + @NotNull + @Contract(value = "_ -> new", pure = true) + private ItemCost convertItemStackToItemCost(@NotNull net.minecraft.world.item.ItemStack itemStack) { + DataComponentPredicate predicate = DataComponentPredicate.allOf(itemStack.getComponents()); + + return new ItemCost(itemStack.getItemHolder(), itemStack.getCount(), predicate, itemStack); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/SmithingTableInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..145f05e97 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/SmithingTableInventoryImpl.java @@ -0,0 +1,335 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.20.6. This is only available for Minecraft 1.20 and higher. + * + * @since 0.10.14 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @NotNull + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 4) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 4, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, message); + + Inventory inventory = containerSmithingTable.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + inventory.setItem(3, items[3]); + + int containerId = containerSmithingTable.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.SMITHING, message)); + serverPlayer.containerMenu = containerSmithingTable; + serverPlayer.initMenu(containerSmithingTable); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.14 + */ + private static class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * Creates a new custom smithing table container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.14 + */ + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + setTitle(title); + + this.checkReachable = false; + + Slot slotOne = this.slots.get(0); + Slot slotTwo = this.slots.get(1); + Slot slotThree = this.slots.get(2); + Slot slotFour = this.slots.get(3); + + this.slots.set(0, new Slot(slotOne.container, slotOne.index, slotOne.x, slotOne.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotOne.onTake(player, stack); + } + }); + + this.slots.set(1, new Slot(slotTwo.container, slotTwo.index, slotTwo.x, slotTwo.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotTwo.onTake(player, stack); + } + }); + + this.slots.set(2, new Slot(slotThree.container, slotThree.index, slotThree.x, slotThree.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotThree.onTake(player, stack); + } + }); + + this.slots.set(3, new Slot(slotFour.container, slotFour.index, slotFour.x, slotFour.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotFour.onTake(player, stack); + } + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @Override + public void createResult() {} + + @Override + protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + + @Override + protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) { + return true; + } + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/StonecutterInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/StonecutterInventoryImpl.java new file mode 100644 index 000000000..62b21ba9b --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/StonecutterInventoryImpl.java @@ -0,0 +1,221 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.20.6 + * + * @since 0.10.14 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items, message); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.14 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, @NotNull Component title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/CustomInventoryUtil.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/CustomInventoryUtil.java new file mode 100644 index 000000000..a0f724f18 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.14 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/TextHolderUtil.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/TextHolderUtil.java new file mode 100644 index 000000000..8e945b8bb --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/TextHolderUtil.java @@ -0,0 +1,67 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.core.HolderLookup; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.14 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson(), HolderLookup.Provider.create(Stream.empty()))); + } +} diff --git a/nms/1_21_0/pom.xml b/nms/1_21_0/pom.xml new file mode 100644 index 000000000..662ae3358 --- /dev/null +++ b/nms/1_21_0/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + 1_21_0 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + org.spigotmc + spigot + 1.21-R0.1-SNAPSHOT + remapped-mojang + provided + + + + + + + net.md-5 + specialsource-maven-plugin + 2.0.3 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:1.21-R0.1-SNAPSHOT:txt:maps-mojang + true + org.spigotmc:spigot:1.21-R0.1-SNAPSHOT:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:1.21-R0.1-SNAPSHOT:csrg:maps-spigot + org.spigotmc:spigot:1.21-R0.1-SNAPSHOT:jar:remapped-obf + + + + + + + \ No newline at end of file diff --git a/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/AnvilInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/AnvilInventoryImpl.java new file mode 100644 index 000000000..b40c02433 --- /dev/null +++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/AnvilInventoryImpl.java @@ -0,0 +1,284 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_0; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_0.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.21.0 + * + * @since 0.10.18 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.18 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.18 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public boolean setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + return true; //no idea what this is for + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/BeaconInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/BeaconInventoryImpl.java new file mode 100644 index 000000000..90981e030 --- /dev/null +++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/BeaconInventoryImpl.java @@ -0,0 +1,202 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_0; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftBeaconView; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.21.0 + * + * @since 0.10.18 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.18 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftBeaconView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + setTitle(Component.empty()); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("s"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftBeaconView getBukkitView() { + if (bukkitEntity == null) { + try { + Container container = (Container) beaconField.get(this); + + Inventory inventory = new CraftInventoryBeacon(container) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + this.bukkitEntity = new CraftBeaconView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/CartographyTableInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..ee1d9a23b --- /dev/null +++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/CartographyTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_0; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.21.0 + * + * @since 0.10.18 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.18 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/EnchantingTableInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..01de8a795 --- /dev/null +++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/EnchantingTableInventoryImpl.java @@ -0,0 +1,220 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_0; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftEnchantmentView; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.21.0 + * + * @since 0.10.18 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.18 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftEnchantmentView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("o"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftEnchantmentView getBukkitView() { + if (bukkitEntity == null) { + try { + Container container = (Container) enchantSlotsField.get(this); + + Inventory inventory = new CraftInventoryEnchanting(container) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + this.bukkitEntity = new CraftEnchantmentView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/GrindstoneInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..59d5f89a5 --- /dev/null +++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/GrindstoneInventoryImpl.java @@ -0,0 +1,208 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_0; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.21.0 + * + * @since 0.10.18 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer, message); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.18 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @param title the title of the gui + * @since 0.10.18 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + setTitle(title); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/MerchantInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/MerchantInventoryImpl.java new file mode 100644 index 000000000..489a02e53 --- /dev/null +++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/MerchantInventoryImpl.java @@ -0,0 +1,118 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_0; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.core.component.DataComponentPredicate; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.ItemCost; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Internal merchant inventory for 1.21.0 + * + * @since 0.10.18 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + ItemCost itemCostA = convertItemStackToItemCost(nmsItemA); + ItemCost itemCostB = convertItemStackToItemCost(nmsItemB); + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + itemCostA, Optional.of(itemCostB), nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Converts an NMS item stack to an item cost. + * + * @param itemStack the item stack to convert + * @return the item cost + * @since 0.10.18 + */ + @NotNull + @Contract(value = "_ -> new", pure = true) + private ItemCost convertItemStackToItemCost(@NotNull net.minecraft.world.item.ItemStack itemStack) { + DataComponentPredicate predicate = DataComponentPredicate.allOf(itemStack.getComponents()); + + return new ItemCost(itemStack.getItemHolder(), itemStack.getCount(), predicate, itemStack); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/SmithingTableInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..b3cb11007 --- /dev/null +++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/SmithingTableInventoryImpl.java @@ -0,0 +1,335 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_0; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_0.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_0.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.21.0. This is only available for Minecraft 1.20 and higher. + * + * @since 0.10.18 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @NotNull + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 4) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 4, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, message); + + Inventory inventory = containerSmithingTable.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + inventory.setItem(3, items[3]); + + int containerId = containerSmithingTable.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.SMITHING, message)); + serverPlayer.containerMenu = containerSmithingTable; + serverPlayer.initMenu(containerSmithingTable); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.18 + */ + private static class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * Creates a new custom smithing table container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.18 + */ + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + setTitle(title); + + this.checkReachable = false; + + Slot slotOne = this.slots.get(0); + Slot slotTwo = this.slots.get(1); + Slot slotThree = this.slots.get(2); + Slot slotFour = this.slots.get(3); + + this.slots.set(0, new Slot(slotOne.container, slotOne.index, slotOne.x, slotOne.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotOne.onTake(player, stack); + } + }); + + this.slots.set(1, new Slot(slotTwo.container, slotTwo.index, slotTwo.x, slotTwo.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotTwo.onTake(player, stack); + } + }); + + this.slots.set(2, new Slot(slotThree.container, slotThree.index, slotThree.x, slotThree.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotThree.onTake(player, stack); + } + }); + + this.slots.set(3, new Slot(slotFour.container, slotFour.index, slotFour.x, slotFour.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotFour.onTake(player, stack); + } + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @Override + public void createResult() {} + + @Override + protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + + @Override + protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) { + return true; + } + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/StonecutterInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/StonecutterInventoryImpl.java new file mode 100644 index 000000000..d12e3e7f3 --- /dev/null +++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/StonecutterInventoryImpl.java @@ -0,0 +1,221 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_0; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_0.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftStonecutterView; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.21.0 + * + * @since 0.10.18 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items, message); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.18 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftStonecutterView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, @NotNull Component title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftStonecutterView getBukkitView() { + if (bukkitEntity == null) { + Inventory inventory = new CraftInventoryStonecutter(this.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + this.bukkitEntity = new CraftStonecutterView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/CustomInventoryUtil.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/CustomInventoryUtil.java new file mode 100644 index 000000000..364fd9bca --- /dev/null +++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_0.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.18 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/TextHolderUtil.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/TextHolderUtil.java new file mode 100644 index 000000000..b0757a95b --- /dev/null +++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/TextHolderUtil.java @@ -0,0 +1,67 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_0.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.core.HolderLookup; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.18 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson(), HolderLookup.Provider.create(Stream.empty()))); + } +} diff --git a/nms/1_21_1/pom.xml b/nms/1_21_1/pom.xml new file mode 100644 index 000000000..a70e9df9f --- /dev/null +++ b/nms/1_21_1/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + 1_21_1 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + org.spigotmc + spigot + 1.21.1-R0.1-SNAPSHOT + remapped-mojang + provided + + + + + + + net.md-5 + specialsource-maven-plugin + 2.0.3 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:1.21.1-R0.1-SNAPSHOT:txt:maps-mojang + true + org.spigotmc:spigot:1.21.1-R0.1-SNAPSHOT:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:1.21.1-R0.1-SNAPSHOT:csrg:maps-spigot + org.spigotmc:spigot:1.21.1-R0.1-SNAPSHOT:jar:remapped-obf + + + + + + + \ No newline at end of file diff --git a/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/AnvilInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/AnvilInventoryImpl.java new file mode 100644 index 000000000..807fb052c --- /dev/null +++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/AnvilInventoryImpl.java @@ -0,0 +1,284 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_1; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_1.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.21.1 + * + * @since 0.10.18 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.18 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.18 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public boolean setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + return true; //no idea what this is for + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/BeaconInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/BeaconInventoryImpl.java new file mode 100644 index 000000000..676d6064c --- /dev/null +++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/BeaconInventoryImpl.java @@ -0,0 +1,201 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_1; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftBeaconView; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.21.1 + * + * @since 0.10.18 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.18 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftBeaconView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + setTitle(Component.empty()); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("s"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftBeaconView getBukkitView() { + if (bukkitEntity == null) { + try { + Container container = (Container) beaconField.get(this); + + org.bukkit.inventory.BeaconInventory inventory = new CraftInventoryBeacon(container) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + this.bukkitEntity = new CraftBeaconView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/CartographyTableInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..f9a79e83e --- /dev/null +++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/CartographyTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_1; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.21.1 + * + * @since 0.10.18 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.18 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/EnchantingTableInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..0e29d26bd --- /dev/null +++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/EnchantingTableInventoryImpl.java @@ -0,0 +1,220 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_1; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftEnchantmentView; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EnchantingInventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.21.1 + * + * @since 0.10.18 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.18 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftEnchantmentView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("o"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftEnchantmentView getBukkitView() { + if (bukkitEntity == null) { + try { + Container container = (Container) enchantSlotsField.get(this); + + EnchantingInventory inventory = new CraftInventoryEnchanting(container) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + this.bukkitEntity = new CraftEnchantmentView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/GrindstoneInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..ba8297b71 --- /dev/null +++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/GrindstoneInventoryImpl.java @@ -0,0 +1,208 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_1; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.21.1 + * + * @since 0.10.18 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer, message); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.18 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @param title the title of the gui + * @since 0.10.18 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + setTitle(title); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/MerchantInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/MerchantInventoryImpl.java new file mode 100644 index 000000000..97aed260a --- /dev/null +++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/MerchantInventoryImpl.java @@ -0,0 +1,118 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_1; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.core.component.DataComponentPredicate; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.ItemCost; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Internal merchant inventory for 1.21.1 + * + * @since 0.10.18 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + ItemCost itemCostA = convertItemStackToItemCost(nmsItemA); + ItemCost itemCostB = convertItemStackToItemCost(nmsItemB); + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + itemCostA, Optional.of(itemCostB), nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Converts an NMS item stack to an item cost. + * + * @param itemStack the item stack to convert + * @return the item cost + * @since 0.10.18 + */ + @NotNull + @Contract(value = "_ -> new", pure = true) + private ItemCost convertItemStackToItemCost(@NotNull net.minecraft.world.item.ItemStack itemStack) { + DataComponentPredicate predicate = DataComponentPredicate.allOf(itemStack.getComponents()); + + return new ItemCost(itemStack.getItemHolder(), itemStack.getCount(), predicate, itemStack); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/SmithingTableInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..1e1cdb77c --- /dev/null +++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/SmithingTableInventoryImpl.java @@ -0,0 +1,335 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_1; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_1.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_1.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.21.1. This is only available for Minecraft 1.20 and higher. + * + * @since 0.10.18 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @NotNull + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 4) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 4, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, message); + + Inventory inventory = containerSmithingTable.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + inventory.setItem(3, items[3]); + + int containerId = containerSmithingTable.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.SMITHING, message)); + serverPlayer.containerMenu = containerSmithingTable; + serverPlayer.initMenu(containerSmithingTable); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.18 + */ + private static class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * Creates a new custom smithing table container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.18 + */ + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + setTitle(title); + + this.checkReachable = false; + + Slot slotOne = this.slots.get(0); + Slot slotTwo = this.slots.get(1); + Slot slotThree = this.slots.get(2); + Slot slotFour = this.slots.get(3); + + this.slots.set(0, new Slot(slotOne.container, slotOne.index, slotOne.x, slotOne.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotOne.onTake(player, stack); + } + }); + + this.slots.set(1, new Slot(slotTwo.container, slotTwo.index, slotTwo.x, slotTwo.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotTwo.onTake(player, stack); + } + }); + + this.slots.set(2, new Slot(slotThree.container, slotThree.index, slotThree.x, slotThree.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotThree.onTake(player, stack); + } + }); + + this.slots.set(3, new Slot(slotFour.container, slotFour.index, slotFour.x, slotFour.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotFour.onTake(player, stack); + } + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @Override + public void createResult() {} + + @Override + protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + + @Override + protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) { + return true; + } + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/StonecutterInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/StonecutterInventoryImpl.java new file mode 100644 index 000000000..85f43a7e0 --- /dev/null +++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/StonecutterInventoryImpl.java @@ -0,0 +1,222 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_1; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_1.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftStonecutterView; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.21.1 + * + * @since 0.10.18 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items, message); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.18 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftStonecutterView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, @NotNull Component title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftStonecutterView getBukkitView() { + if (bukkitEntity == null) { + org.bukkit.inventory.StonecutterInventory inventory = new CraftInventoryStonecutter( + this.container, getResultInventory() + ) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + this.bukkitEntity = new CraftStonecutterView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/CustomInventoryUtil.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/CustomInventoryUtil.java new file mode 100644 index 000000000..e1d0af6d9 --- /dev/null +++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_1.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.18 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/TextHolderUtil.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/TextHolderUtil.java new file mode 100644 index 000000000..16d0416fd --- /dev/null +++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/TextHolderUtil.java @@ -0,0 +1,67 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_1.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.core.HolderLookup; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.18 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson(), HolderLookup.Provider.create(Stream.empty()))); + } +} diff --git a/nms/1_21_2-3/pom.xml b/nms/1_21_2-3/pom.xml new file mode 100644 index 000000000..a042895c3 --- /dev/null +++ b/nms/1_21_2-3/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.18 + ../../pom.xml + + + 1_21_2-3 + + + true + + + + + com.github.stefvanschie.inventoryframework + abstraction + ${project.version} + compile + + + org.spigotmc + spigot + 1.21.3-R0.1-SNAPSHOT + remapped-mojang + provided + + + + + + + net.md-5 + specialsource-maven-plugin + 2.0.3 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:1.21.3-R0.1-SNAPSHOT:txt:maps-mojang + true + org.spigotmc:spigot:1.21.3-R0.1-SNAPSHOT:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:1.21.3-R0.1-SNAPSHOT:csrg:maps-spigot + org.spigotmc:spigot:1.21.3-R0.1-SNAPSHOT:jar:remapped-obf + + + + + + + \ No newline at end of file diff --git a/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/AnvilInventoryImpl.java b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/AnvilInventoryImpl.java new file mode 100644 index 000000000..0b3aa092f --- /dev/null +++ b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/AnvilInventoryImpl.java @@ -0,0 +1,284 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal anvil inventory for 1.21.2 + * + * @since 0.10.18 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for an anvil should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.18 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.18 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @Override + public boolean setItemName(@Nullable String name) { + name = name == null ? "" : name; + + /* Only update if the name is actually different. This may be called even if the name is not different, + particularly when putting an item in the first slot. */ + if (!name.equals(AnvilInventoryImpl.super.observableText.get())) { + AnvilInventoryImpl.super.observableText.set(name); + } + + //the client predicts the output result, so we broadcast the state again to override it + broadcastFullState(); + return true; //no idea what this is for + } + + @Override + public void createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/BeaconInventoryImpl.java b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/BeaconInventoryImpl.java new file mode 100644 index 000000000..34faf64cb --- /dev/null +++ b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/BeaconInventoryImpl.java @@ -0,0 +1,201 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R2.inventory.view.CraftBeaconView; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal beacon inventory for 1.21.2 + * + * @since 0.10.18 + */ +public class BeaconInventoryImpl extends BeaconInventory { + + public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container beacon + * + * @since 0.10.18 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * The player for this beacon container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container beacon + */ + @Nullable + private CraftBeaconView bukkitEntity; + + /** + * Field for accessing the beacon field + */ + @NotNull + private final Field beaconField; + + public ContainerBeaconImpl(@NotNull ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + setTitle(Component.empty()); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("u"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) beaconField.get(this)).setItem(0, itemStack); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + @NotNull + @Override + public CraftBeaconView getBukkitView() { + if (bukkitEntity == null) { + try { + Container container = (Container) beaconField.get(this); + + org.bukkit.inventory.BeaconInventory inventory = new CraftInventoryBeacon(container) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + this.bukkitEntity = new CraftBeaconView(player, inventory, this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/CartographyTableInventoryImpl.java b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/CartographyTableInventoryImpl.java new file mode 100644 index 000000000..ef2b9a2bb --- /dev/null +++ b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/CartographyTableInventoryImpl.java @@ -0,0 +1,218 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal cartography table inventory for 1.21.2 + * + * @since 0.10.18 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'" + ); + } + + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container cartography table + * + * @since 0.10.18 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * The player for this cartography table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container cartography table + */ + @Nullable + private CraftInventoryView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("w"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.setItem(1, CraftItemStack.asNMSCopy(items[1])); + + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2])); + } + + @NotNull + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + CraftInventory inventory = new CraftInventoryCartography(super.container, getResultInventory()) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + bukkitEntity = new CraftInventoryView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} diff --git a/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/EnchantingTableInventoryImpl.java b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/EnchantingTableInventoryImpl.java new file mode 100644 index 000000000..c4cec2658 --- /dev/null +++ b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/EnchantingTableInventoryImpl.java @@ -0,0 +1,220 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R2.inventory.view.CraftEnchantmentView; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EnchantingInventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal enchanting table inventory for 1.21.2 + * + * @since 0.10.18 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.18 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftEnchantmentView bukkitEntity; + + /** + * Field for accessing the enchant slots field + */ + @NotNull + private final Field enchantSlotsField; + + public ContainerEnchantingTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("q"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) enchantSlotsField.get(this); + + input.setItem(0, CraftItemStack.asNMSCopy(items[0])); + input.setItem(1, CraftItemStack.asNMSCopy(items[1])); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public CraftEnchantmentView getBukkitView() { + if (bukkitEntity == null) { + try { + Container container = (Container) enchantSlotsField.get(this); + + EnchantingInventory inventory = new CraftInventoryEnchanting(container) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + this.bukkitEntity = new CraftEnchantmentView(player, inventory, this); + } catch (IllegalAccessException exception) { + exception.printStackTrace(); + } + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + } +} diff --git a/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/GrindstoneInventoryImpl.java b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/GrindstoneInventoryImpl.java new file mode 100644 index 000000000..c9796f497 --- /dev/null +++ b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/GrindstoneInventoryImpl.java @@ -0,0 +1,208 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal grindstone inventory for 1.21.1 + * + * @since 0.10.18 + */ +public class GrindstoneInventoryImpl extends GrindstoneInventory { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 3) { + throw new IllegalArgumentException( + "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer, message); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container grindstone + * + * @since 0.10.18 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @param title the title of the gui + * @since 0.10.18 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + setTitle(title); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/MerchantInventoryImpl.java b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/MerchantInventoryImpl.java new file mode 100644 index 000000000..7f64c8787 --- /dev/null +++ b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/MerchantInventoryImpl.java @@ -0,0 +1,118 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.core.component.DataComponentPredicate; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.ItemCost; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Internal merchant inventory for 1.21.2 + * + * @since 0.10.18 + */ +public class MerchantInventoryImpl extends MerchantInventory { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry entry : trades) { + MerchantRecipe recipe = entry.getKey(); + List ingredients = recipe.getIngredients(); + + if (ingredients.size() < 1) { + throw new IllegalStateException("Merchant recipe has no ingredients"); + } + + ItemStack itemA = ingredients.get(0); + ItemStack itemB = null; + + if (ingredients.size() >= 2) { + itemB = ingredients.get(1); + } + + net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA); + net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult()); + + if (itemB != null) { + nmsItemB = CraftItemStack.asNMSCopy(itemB); + } + + ItemCost itemCostA = convertItemStackToItemCost(nmsItemA); + ItemCost itemCostB = convertItemStackToItemCost(nmsItemB); + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + int exp = recipe.getVillagerExperience(); + float multiplier = recipe.getPriceMultiplier(); + + MerchantOffer merchantOffer = new MerchantOffer( + itemCostA, Optional.of(itemCostB), nmsItemResult, uses, maxUses, exp, multiplier + ); + merchantOffer.setSpecialPriceDiff(entry.getValue()); + + offers.add(merchantOffer); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + + serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false); + } + + /** + * Converts an NMS item stack to an item cost. + * + * @param itemStack the item stack to convert + * @return the item cost + * @since 0.10.18 + */ + @NotNull + @Contract(value = "_ -> new", pure = true) + private ItemCost convertItemStackToItemCost(@NotNull net.minecraft.world.item.ItemStack itemStack) { + DataComponentPredicate predicate = DataComponentPredicate.allOf(itemStack.getComponents()); + + return new ItemCost(itemStack.getItemHolder(), itemStack.getCount(), predicate, itemStack); + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * Gets the containerId id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the containerId id for + * @return the containerId id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } +} diff --git a/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/SmithingTableInventoryImpl.java b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/SmithingTableInventoryImpl.java new file mode 100644 index 000000000..03457ad75 --- /dev/null +++ b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/SmithingTableInventoryImpl.java @@ -0,0 +1,335 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.SmithingMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Internal smithing table inventory for 1.21.2. This is only available for Minecraft 1.20 and higher. + * + * @since 0.10.18 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @NotNull + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 4) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 4, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, message); + + Inventory inventory = containerSmithingTable.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + inventory.setItem(3, items[3]); + + int containerId = containerSmithingTable.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.SMITHING, message)); + serverPlayer.containerMenu = containerSmithingTable; + serverPlayer.initMenu(containerSmithingTable); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 1, nmsItem)); + } + + @Override + public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + sendResultItem(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearResultItem(@NotNull Player player) { + sendResultItem(player, ItemStack.EMPTY); + } + + @Override + public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) { + setCursor(player, CraftItemStack.asNMSCopy(item)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Sets the cursor of the given player + * + * @param player the player to set the cursor + * @param item the item to set the cursor to + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, item)); + } + + /** + * Sends the result item to the specified player with the given item + * + * @param player the player to send the result item to + * @param item the result item + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container smithing table + * + * @since 0.10.18 + */ + private static class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * Creates a new custom smithing table container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.18 + */ + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + setTitle(title); + + this.checkReachable = false; + + Slot slotOne = this.slots.get(0); + Slot slotTwo = this.slots.get(1); + Slot slotThree = this.slots.get(2); + Slot slotFour = this.slots.get(3); + + this.slots.set(0, new Slot(slotOne.container, slotOne.index, slotOne.x, slotOne.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotOne.onTake(player, stack); + } + }); + + this.slots.set(1, new Slot(slotTwo.container, slotTwo.index, slotTwo.x, slotTwo.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotTwo.onTake(player, stack); + } + }); + + this.slots.set(2, new Slot(slotThree.container, slotThree.index, slotThree.x, slotThree.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotThree.onTake(player, stack); + } + }); + + this.slots.set(3, new Slot(slotFour.container, slotFour.index, slotFour.x, slotFour.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotFour.onTake(player, stack); + } + }); + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + @Override + public void createResult() {} + + @Override + protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + + @Override + protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) { + return true; + } + + public int getContainerId() { + return this.containerId; + } + } +} diff --git a/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/StonecutterInventoryImpl.java b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/StonecutterInventoryImpl.java new file mode 100644 index 000000000..18d570277 --- /dev/null +++ b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/StonecutterInventoryImpl.java @@ -0,0 +1,222 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_2_3; + +import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R2.inventory.view.CraftStonecutterView; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * Internal stonecutter inventory for 1.21.2 + * + * @since 0.10.18 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 2) { + throw new IllegalArgumentException( + "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items, message); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.18 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * Gets the server player associated to this player + * + * @param player the player to get the server player from + * @return the server player + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private ServerPlayer getServerPlayer(@NotNull Player player) { + return ((CraftPlayer) player).getHandle(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.18 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * The player for this enchanting table container + */ + @NotNull + private final Player player; + + /** + * The internal bukkit entity for this container enchanting table + */ + @Nullable + private CraftStonecutterView bukkitEntity; + + /** + * Field for accessing the result inventory field + */ + @NotNull + private final Field resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, @NotNull Component title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("C"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1])); + } + + @NotNull + @Override + public CraftStonecutterView getBukkitView() { + if (bukkitEntity == null) { + org.bukkit.inventory.StonecutterInventory inventory = new CraftInventoryStonecutter( + this.container, getResultInventory() + ) { + @NotNull + @Contract(pure = true) + @Override + public InventoryHolder getHolder() { + return inventoryHolder; + } + }; + + this.bukkitEntity = new CraftStonecutterView(player, inventory, this); + } + + return bukkitEntity; + } + + @Contract(pure = true, value = "_ -> true") + @Override + public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) { + return true; + } + + @Override + public void slotsChanged(Container container) {} + + @Override + public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} diff --git a/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/util/CustomInventoryUtil.java b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/util/CustomInventoryUtil.java new file mode 100644 index 000000000..eb2257b95 --- /dev/null +++ b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/util/CustomInventoryUtil.java @@ -0,0 +1,41 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A utility class for custom inventories + * + * @since 0.10.18 + */ +public final class CustomInventoryUtil { + + /** + * A private constructor to prevent construction. + */ + private CustomInventoryUtil() {} + + /** + * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items + * were specified, this returns an empty list. + * + * @param items the items to convert + * @return a list of converted items + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) { + NonNullList nmsItems = NonNullList.create(); + + for (org.bukkit.inventory.ItemStack item : items) { + nmsItems.add(CraftItemStack.asNMSCopy(item)); + } + + return nmsItems; + } +} diff --git a/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/util/TextHolderUtil.java b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/util/TextHolderUtil.java new file mode 100644 index 000000000..4f8075836 --- /dev/null +++ b/nms/1_21_2-3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_2_3/util/TextHolderUtil.java @@ -0,0 +1,67 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.util; + +import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; +import net.minecraft.core.HolderLookup; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * A utility class for adding {@link TextHolder} support. + * + * @since 0.10.18 + */ +public final class TextHolderUtil { + + private TextHolderUtil() { + //private constructor to prevent construction + } + + /** + * Converts the specified value to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + public static Component toComponent(@NotNull TextHolder holder) { + if (holder instanceof StringHolder) { + return toComponent((StringHolder) holder); + } else { + return toComponent((ComponentHolder) holder); + } + } + + /** + * Converts the specified legacy string holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull StringHolder holder) { + return Component.literal(holder.asLegacyString()); + } + + /** + * Converts the specified Adventure component holder to a vanilla component. + * + * @param holder the value to convert + * @return the value as a vanilla component + * @since 0.10.18 + */ + @NotNull + @Contract(pure = true) + private static Component toComponent(@NotNull ComponentHolder holder) { + return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson(), HolderLookup.Provider.create(Stream.empty()))); + } +} diff --git a/nms/abstraction/pom.xml b/nms/abstraction/pom.xml index e0566382b..3d194bfef 100644 --- a/nms/abstraction/pom.xml +++ b/nms/abstraction/pom.xml @@ -5,7 +5,7 @@ IF-parent com.github.stefvanschie.inventoryframework - 0.9.8 + 0.10.18 ../../pom.xml 4.0.0 @@ -24,6 +24,12 @@ + + com.github.stefvanschie.inventoryframework + adventure-support + ${project.version} + compile + org.spigotmc spigot-api diff --git a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/AnvilInventory.java b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/AnvilInventory.java index e70fdf48e..44f5bfb7b 100644 --- a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/AnvilInventory.java +++ b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/AnvilInventory.java @@ -1,12 +1,18 @@ package com.github.stefvanschie.inventoryframework.abstraction; +import com.github.stefvanschie.inventoryframework.abstraction.util.ObservableValue; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.function.Consumer; + /** * An anvil inventory * @@ -22,10 +28,24 @@ public abstract class AnvilInventory { /** * The rename text + * + * @deprecated Superseded by {@link #observableText} */ @NotNull + @Deprecated protected String text = ""; + /** + * The name input text. + */ + @NotNull + protected final ObservableValue<@NotNull String> observableText = new ObservableValue<>(""); + + /** + * The enchantment cost displayed + */ + protected short cost; + /** * Creates a new anvil inventory for the specified inventory holder * @@ -36,6 +56,23 @@ public AnvilInventory(@NotNull InventoryHolder inventoryHolder) { this.inventoryHolder = inventoryHolder; } + /** + * Sets the enchantment level cost for this anvil gui. Taking the item from the result slot will not actually remove + * these levels. Having a cost specified does not impede a player's ability to take the item in the result item, + * even if the player does not have the specified amount of levels. The cost must be a non-negative number. + * + * @param cost the cost + * @since 0.10.8 + * @throws IllegalArgumentException when the cost is less than zero + */ + public void setCost(short cost) { + if (cost < 0){ + throw new IllegalArgumentException("Cost must be non-negative"); + } + + this.cost = cost; + } + /** * Opens the inventory for the specified player * @@ -44,7 +81,11 @@ public AnvilInventory(@NotNull InventoryHolder inventoryHolder) { * @param items the items to show * @since 0.8.0 */ - public abstract void openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items); + public final void openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items) { + openInventory(player, StringHolder.of(title), items); + } + + public abstract Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, @Nullable ItemStack[] items); /** * Sends the top items to the inventory for the specified player. @@ -52,7 +93,9 @@ public AnvilInventory(@NotNull InventoryHolder inventoryHolder) { * @param player the player for which to open the anvil * @param items the items to send * @since 0.8.0 + * @deprecated no longer used internally */ + @Deprecated public abstract void sendItems(@NotNull Player player, @Nullable ItemStack[] items); /** @@ -61,7 +104,9 @@ public AnvilInventory(@NotNull InventoryHolder inventoryHolder) { * @param player the player to send the item to * @param item the item to send * @since 0.8.0 + * @deprecated no longer used internally */ + @Deprecated public abstract void sendResultItem(@NotNull Player player, @Nullable ItemStack item); /** @@ -70,7 +115,9 @@ public AnvilInventory(@NotNull InventoryHolder inventoryHolder) { * @param player the player to send the item to * @param item the item to send * @since 0.8.0 + * @deprecated no longer used internally */ + @Deprecated public abstract void sendFirstItem(@NotNull Player player, @Nullable ItemStack item); /** @@ -79,7 +126,9 @@ public AnvilInventory(@NotNull InventoryHolder inventoryHolder) { * @param player the player to send the item to * @param item the item to send * @since 0.8.0 + * @deprecated no longer used internally */ + @Deprecated public abstract void sendSecondItem(@NotNull Player player, @Nullable ItemStack item); /** @@ -88,7 +137,9 @@ public AnvilInventory(@NotNull InventoryHolder inventoryHolder) { * @param player the player to set the cursor * @param item the item to set the cursor to * @since 0.8.0 + * @deprecated no longer used internally */ + @Deprecated public abstract void setCursor(@NotNull Player player, @NotNull ItemStack item); /** @@ -96,7 +147,9 @@ public AnvilInventory(@NotNull InventoryHolder inventoryHolder) { * * @param player the player to clear the cursor of * @since 0.8.0 + * @deprecated no longer used internally */ + @Deprecated public abstract void clearCursor(@NotNull Player player); /** @@ -104,7 +157,9 @@ public AnvilInventory(@NotNull InventoryHolder inventoryHolder) { * * @param player the player to clear the result item of * @since 0.8.0 + * @deprecated no longer used internally */ + @Deprecated public abstract void clearResultItem(@NotNull Player player); /** @@ -116,6 +171,22 @@ public AnvilInventory(@NotNull InventoryHolder inventoryHolder) { @NotNull @Contract(pure = true) public String getRenameText() { + String text = observableText.get(); + + if (text == null) { + throw new IllegalStateException("Rename text is null"); + } + return text; } + + /** + * Subscribes to changes of the name input. + * + * @param onNameInputChanged the consumer to call when the name input changes + * @since 0.10.10 + */ + public void subscribeToNameInputChanges(@NotNull Consumer onNameInputChanged) { + this.observableText.subscribe(onNameInputChanged); + } } diff --git a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/CartographyTableInventory.java b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/CartographyTableInventory.java index da4f1db58..586ecc61a 100644 --- a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/CartographyTableInventory.java +++ b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/CartographyTableInventory.java @@ -1,5 +1,7 @@ package com.github.stefvanschie.inventoryframework.abstraction; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; @@ -37,7 +39,11 @@ public CartographyTableInventory(@NotNull InventoryHolder inventoryHolder) { * @param items the top items of the inventory * @since 0.8.0 */ - public abstract void openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items); + public final void openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items) { + openInventory(player, StringHolder.of(title), items); + } + + public abstract void openInventory(@NotNull Player player, @NotNull TextHolder title, @Nullable ItemStack[] items); /** * Sends the top items to the inventory for the specified player. diff --git a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/EnchantingTableInventory.java b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/EnchantingTableInventory.java index dc8b97bf0..d60093350 100644 --- a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/EnchantingTableInventory.java +++ b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/EnchantingTableInventory.java @@ -1,5 +1,7 @@ package com.github.stefvanschie.inventoryframework.abstraction; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; @@ -37,7 +39,11 @@ public EnchantingTableInventory(@NotNull InventoryHolder inventoryHolder) { * @param items the top items * @since 0.8.0 */ - public abstract void openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items); + public final void openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items) { + openInventory(player, StringHolder.of(title), items); + } + + public abstract void openInventory(@NotNull Player player, @NotNull TextHolder title, @Nullable ItemStack[] items); /** * Sends the top items to the inventory for the specified player. diff --git a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/GrindstoneInventory.java b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/GrindstoneInventory.java index 314eb1996..e9b3571b3 100644 --- a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/GrindstoneInventory.java +++ b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/GrindstoneInventory.java @@ -1,6 +1,9 @@ package com.github.stefvanschie.inventoryframework.abstraction; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -37,22 +40,31 @@ public GrindstoneInventory(@NotNull InventoryHolder inventoryHolder) { * @param items the top items * @since 0.8.0 */ - public abstract void openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items); + public final Inventory openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items) { + return openInventory(player, StringHolder.of(title), items); + } + + public abstract Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, @Nullable ItemStack[] items); /** * Sends the top items to the inventory for the specified player. * * @param player the player for which to open the grindstone * @param items the items to send + * @param cursor the cursor item, this may be null on versions prior to 1.17.1 * @since 0.8.0 + * @deprecated no longer used internally */ - public abstract void sendItems(@NotNull Player player, @Nullable ItemStack[] items); + @Deprecated + public abstract void sendItems(@NotNull Player player, @Nullable ItemStack[] items, @Nullable ItemStack cursor); /** * Clears the cursor of the specified player * * @param player the player to clear the cursor of * @since 0.8.0 + * @deprecated no longer used internally */ + @Deprecated public abstract void clearCursor(@NotNull Player player); } diff --git a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/MerchantInventory.java b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/MerchantInventory.java new file mode 100644 index 000000000..da5402043 --- /dev/null +++ b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/MerchantInventory.java @@ -0,0 +1,29 @@ +package com.github.stefvanschie.inventoryframework.abstraction; + +import org.bukkit.entity.Player; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * A merchant inventory + * + * @since 0.10.1 + */ +public abstract class MerchantInventory { + + /** + * Sends the merchant offers to the player, combined with the merchants level and experience. + * + * @param player the player to send this to + * @param trades the trades to send + * @param level the level of the merchant + * @param experience the experience of the merchant + * @since 0.10.1 + */ + public abstract void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience); +} diff --git a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/SmithingTableInventory.java b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/SmithingTableInventory.java index e34f65ea0..267d73a01 100644 --- a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/SmithingTableInventory.java +++ b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/SmithingTableInventory.java @@ -1,6 +1,9 @@ package com.github.stefvanschie.inventoryframework.abstraction; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -37,16 +40,32 @@ public SmithingTableInventory(@NotNull InventoryHolder inventoryHolder) { * @param items the top items * @since 0.8.0 */ - public abstract void openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items); + public final void openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items) { + openInventory(player, StringHolder.of(title), items); + } + + /** + * Opens the inventory for the specified player + * + * @param player the player to open the inventory for + * @param title the title of the inventory + * @param items the top items + * @return the opened inventory + * @since 0.10.9 + */ + @Nullable + public abstract Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable ItemStack[] items); /** * Sends the top items to the inventory for the specified player. * * @param player the player for which to open the smithing table * @param items the items to send + * @param cursor the cursor item, this may be null on versions prior to 1.17.1 * @since 0.8.0 */ - public abstract void sendItems(@NotNull Player player, @Nullable ItemStack[] items); + public abstract void sendItems(@NotNull Player player, @Nullable ItemStack[] items, @Nullable ItemStack cursor); /** * Sends the result item to the specified player diff --git a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/StonecutterInventory.java b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/StonecutterInventory.java index 7481979d8..5e2450c12 100644 --- a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/StonecutterInventory.java +++ b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/StonecutterInventory.java @@ -1,5 +1,7 @@ package com.github.stefvanschie.inventoryframework.abstraction; +import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder; +import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; @@ -37,7 +39,11 @@ public StonecutterInventory(@NotNull InventoryHolder inventoryHolder) { * @param items the top items * @since 0.8.0 */ - public abstract void openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items); + public final void openInventory(@NotNull Player player, @NotNull String title, @Nullable ItemStack[] items) { + openInventory(player, StringHolder.of(title), items); + } + + public abstract void openInventory(@NotNull Player player, @NotNull TextHolder title, @Nullable ItemStack[] items); /** * Sends the top items to the inventory for the specified player. diff --git a/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/util/ObservableValue.java b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/util/ObservableValue.java new file mode 100644 index 000000000..c0d5309f9 --- /dev/null +++ b/nms/abstraction/src/main/java/com/github/stefvanschie/inventoryframework/abstraction/util/ObservableValue.java @@ -0,0 +1,80 @@ +package com.github.stefvanschie.inventoryframework.abstraction.util; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.HashSet; +import java.util.function.Consumer; + +/** + * A value whose modifications can be observed. + * + * @param the type of value to store + * @since 0.10.10 + */ +public class ObservableValue { + + /** + * A collection of subscribers that should be notified on updates. + */ + @NotNull + private final Collection> subscribers = new HashSet<>(); + + /** + * The current value + */ + @Nullable + private T value; + + /** + * Creates a new observable value with the given default value. + * + * @param defaultValue the default value + * @since 0.10.10 + */ + public ObservableValue(@Nullable T defaultValue) { + this.value = defaultValue; + } + + /** + * Updates the old value to the given new value. This will notify all the subscribers before updating the new value. + * Subscribers may observe the old value by using {@link #get()}. This will always notify the subscribers, even if + * the new value is the same as the old value. + * + * @param newValue the new value + * @since 0.10.10 + */ + public void set(T newValue) { + for (Consumer subscriber : this.subscribers) { + subscriber.accept(newValue); + } + + this.value = newValue; + } + + /** + * Subscribes to modifications of this value. The provided consumer will be called every time this value changes. + * + * @param consumer the consumer to call upon updates of this value + * @since 0.10.10 + */ + public void subscribe(@NotNull Consumer consumer) { + this.subscribers.add(consumer); + } + + /** + * Gets the current value of this item. If this is called from within a subscriber, then this is the value from + * before the current in-progress update. + * + * @return the current value + * @since 0.10.10 + */ + @Nullable + @Contract(pure = true) + public T get() { + return this.value; + } + +} diff --git a/pom.xml b/pom.xml index 37b63b40e..7050fa7b4 100644 --- a/pom.xml +++ b/pom.xml @@ -7,11 +7,34 @@ IF nms/abstraction - nms/1_16_R3 - nms/1_16_R2 - nms/1_16_R1 - nms/1_15_R1 - nms/1_14_R1 + nms/1_21_2-3 + nms/1_21_1 + nms/1_21_0 + nms/1_20_6 + nms/1_20_5 + nms/1_20_3-4 + nms/1_20_2 + nms/1_20_1 + nms/1_20_0 + nms/1_19_4 + nms/1_19_3 + nms/1_19_2 + nms/1_19_1 + nms/1_19_0 + nms/1_18_2 + nms/1_18_1 + nms/1_18_0 + nms/1_17_1 + nms/1_17_0 + nms/1_16_4-5 + nms/1_16_2-3 + nms/1_16_1 + nms/1_15 + nms/1_14 + adventure-support + inventory-view/iv-abstract-class + inventory-view/iv-abstraction + inventory-view/iv-interface @@ -19,11 +42,12 @@ 1.8 true UTF-8 + 4.17.0 com.github.stefvanschie.inventoryframework IF-parent - 0.9.8 + 0.10.18 pom IF @@ -53,7 +77,7 @@ org.jetbrains annotations - 19.0.0 + 24.1.0 provided @@ -77,7 +101,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.2.5 sign-artifacts @@ -98,24 +122,24 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.0 + 3.5.2 - org.junit.platform - junit-platform-surefire-provider - 1.2.0-M1 + org.apache.maven.surefire + surefire-junit-platform + 3.5.2 org.junit.jupiter junit-jupiter-engine - 5.2.0 + 5.11.0 org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.7.0 true ossrh @@ -126,7 +150,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.0 + 3.3.1 attach-sources @@ -139,7 +163,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.1.1 + 3.10.0 attach-javadocs @@ -148,6 +172,12 @@ + + + https://hub.spigotmc.org/javadocs/bukkit/ + https://jd.adventure.kyori.net/api/4.10.0/ + +